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)
2341 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2345 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2347 Gdk::Cursor* cursor;
2348 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2350 float x_fraction = cnote->mouse_x_fraction ();
2352 if (x_fraction > 0.0 && x_fraction < 0.25) {
2353 cursor = _editor->cursors()->left_side_trim;
2356 cursor = _editor->cursors()->right_side_trim;
2360 Drag::start_grab (event, cursor);
2362 region = &cnote->region_view();
2365 temp = region->snap_to_pixel (cnote->x0 (), true);
2366 _snap_delta = temp - cnote->x0 ();
2370 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2375 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2376 if (ms.size() > 1) {
2377 /* has to be relative, may make no sense otherwise */
2380 /* select this note; if it is already selected, preserve the existing selection,
2381 otherwise make this note the only one selected.
2383 region->note_selected (cnote, cnote->selected ());
2387 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2389 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2391 _editor->begin_reversible_command (_("resize notes"));
2393 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2394 MidiRegionSelection::iterator next;
2397 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2399 mrv->begin_resizing (at_front);
2405 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2406 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2408 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2412 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2414 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2415 if (_editor->snap_mode () != SnapOff) {
2419 if (_editor->snap_mode () == SnapOff) {
2421 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2422 if (apply_snap_delta) {
2428 if (apply_snap_delta) {
2432 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2438 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2440 if (!movement_occurred) {
2444 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2445 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2446 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2448 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2451 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2453 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2454 if (_editor->snap_mode () != SnapOff) {
2458 if (_editor->snap_mode () == SnapOff) {
2460 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2461 if (apply_snap_delta) {
2467 if (apply_snap_delta) {
2471 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2475 _editor->commit_reversible_command ();
2479 NoteResizeDrag::aborted (bool)
2481 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2482 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2483 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2485 mrv->abort_resizing ();
2490 AVDraggingView::AVDraggingView (RegionView* v)
2493 initial_position = v->region()->position ();
2496 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2499 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2502 TrackViewList empty;
2504 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2505 std::list<RegionView*> views = rs.by_layer();
2507 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2508 RegionView* rv = (*i);
2509 if (!rv->region()->video_locked()) {
2512 _views.push_back (AVDraggingView (rv));
2517 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2519 Drag::start_grab (event);
2520 if (_editor->session() == 0) {
2524 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2525 _max_backwards_drag = (
2526 ARDOUR_UI::instance()->video_timeline->get_duration()
2527 + ARDOUR_UI::instance()->video_timeline->get_offset()
2528 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2531 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2532 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2533 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2536 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2539 Timecode::Time timecode;
2540 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2541 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);
2542 show_verbose_cursor_text (buf);
2546 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2548 if (_editor->session() == 0) {
2551 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2555 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2556 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2558 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2559 dt = - _max_backwards_drag;
2562 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2563 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2565 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2566 RegionView* rv = i->view;
2567 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2570 rv->region()->clear_changes ();
2571 rv->region()->suspend_property_changes();
2573 rv->region()->set_position(i->initial_position + dt);
2574 rv->region_changed(ARDOUR::Properties::position);
2577 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2578 Timecode::Time timecode;
2579 Timecode::Time timediff;
2581 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2582 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2583 snprintf (buf, sizeof (buf),
2584 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2585 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2586 , _("Video Start:"),
2587 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2589 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2591 show_verbose_cursor_text (buf);
2595 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2597 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2601 if (!movement_occurred || ! _editor->session()) {
2605 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2607 _editor->begin_reversible_command (_("Move Video"));
2609 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2610 ARDOUR_UI::instance()->video_timeline->save_undo();
2611 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2612 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2614 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2615 i->view->drag_end();
2616 i->view->region()->resume_property_changes ();
2618 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2621 _editor->session()->maybe_update_session_range(
2622 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2623 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2627 _editor->commit_reversible_command ();
2631 VideoTimeLineDrag::aborted (bool)
2633 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2636 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2637 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2639 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2640 i->view->region()->resume_property_changes ();
2641 i->view->region()->set_position(i->initial_position);
2645 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2646 : RegionDrag (e, i, p, v)
2647 , _preserve_fade_anchor (preserve_fade_anchor)
2648 , _jump_position_when_done (false)
2650 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2654 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2657 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2658 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2660 if (tv && tv->is_track()) {
2661 speed = tv->track()->speed();
2664 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2665 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2666 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2668 framepos_t const pf = adjusted_current_frame (event);
2669 setup_snap_delta (region_start);
2671 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2672 /* Move the contents of the region around without changing the region bounds */
2673 _operation = ContentsTrim;
2674 Drag::start_grab (event, _editor->cursors()->trimmer);
2676 /* These will get overridden for a point trim.*/
2677 if (pf < (region_start + region_length/2)) {
2678 /* closer to front */
2679 _operation = StartTrim;
2680 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2681 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2683 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2687 _operation = EndTrim;
2688 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2689 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2691 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2695 /* jump trim disabled for now
2696 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2697 _jump_position_when_done = true;
2701 switch (_operation) {
2703 show_verbose_cursor_time (region_start);
2704 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2705 i->view->trim_front_starting ();
2709 show_verbose_cursor_duration (region_start, region_end);
2712 show_verbose_cursor_time (pf);
2716 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2717 i->view->region()->suspend_property_changes ();
2722 TrimDrag::motion (GdkEvent* event, bool first_move)
2724 RegionView* rv = _primary;
2727 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2728 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2729 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2730 frameoffset_t frame_delta = 0;
2732 if (tv && tv->is_track()) {
2733 speed = tv->track()->speed();
2735 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2736 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2742 switch (_operation) {
2744 trim_type = "Region start trim";
2747 trim_type = "Region end trim";
2750 trim_type = "Region content trim";
2757 _editor->begin_reversible_command (trim_type);
2759 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2760 RegionView* rv = i->view;
2761 rv->enable_display (false);
2762 rv->region()->playlist()->clear_owned_changes ();
2764 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2767 arv->temporarily_hide_envelope ();
2771 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2772 insert_result = _editor->motion_frozen_playlists.insert (pl);
2774 if (insert_result.second) {
2780 bool non_overlap_trim = false;
2782 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2783 non_overlap_trim = true;
2786 /* contstrain trim to fade length */
2787 if (_preserve_fade_anchor) {
2788 switch (_operation) {
2790 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2791 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2793 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2794 if (ar->locked()) continue;
2795 framecnt_t len = ar->fade_in()->back()->when;
2796 if (len < dt) dt = min(dt, len);
2800 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2801 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2803 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2804 if (ar->locked()) continue;
2805 framecnt_t len = ar->fade_out()->back()->when;
2806 if (len < -dt) dt = max(dt, -len);
2815 switch (_operation) {
2817 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2818 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2819 if (changed && _preserve_fade_anchor) {
2820 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2822 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2823 framecnt_t len = ar->fade_in()->back()->when;
2824 framecnt_t diff = ar->first_frame() - i->initial_position;
2825 framepos_t new_length = len - diff;
2826 i->anchored_fade_length = min (ar->length(), new_length);
2827 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2828 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2835 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2836 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2837 if (changed && _preserve_fade_anchor) {
2838 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2840 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2841 framecnt_t len = ar->fade_out()->back()->when;
2842 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2843 framepos_t new_length = len + diff;
2844 i->anchored_fade_length = min (ar->length(), new_length);
2845 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2846 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2854 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2856 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2857 i->view->move_contents (frame_delta);
2863 switch (_operation) {
2865 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2868 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2871 // show_verbose_cursor_time (frame_delta);
2877 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2879 if (movement_occurred) {
2880 motion (event, false);
2882 if (_operation == StartTrim) {
2883 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2885 /* This must happen before the region's StatefulDiffCommand is created, as it may
2886 `correct' (ahem) the region's _start from being negative to being zero. It
2887 needs to be zero in the undo record.
2889 i->view->trim_front_ending ();
2891 if (_preserve_fade_anchor) {
2892 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2894 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2895 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2896 ar->set_fade_in_length(i->anchored_fade_length);
2897 ar->set_fade_in_active(true);
2900 if (_jump_position_when_done) {
2901 i->view->region()->set_position (i->initial_position);
2904 } else if (_operation == EndTrim) {
2905 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2906 if (_preserve_fade_anchor) {
2907 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2909 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2910 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2911 ar->set_fade_out_length(i->anchored_fade_length);
2912 ar->set_fade_out_active(true);
2915 if (_jump_position_when_done) {
2916 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2921 if (!_views.empty()) {
2922 if (_operation == StartTrim) {
2923 _editor->maybe_locate_with_edit_preroll(
2924 _views.begin()->view->region()->position());
2926 if (_operation == EndTrim) {
2927 _editor->maybe_locate_with_edit_preroll(
2928 _views.begin()->view->region()->position() +
2929 _views.begin()->view->region()->length());
2933 if (!_editor->selection->selected (_primary)) {
2934 _primary->thaw_after_trim ();
2937 set<boost::shared_ptr<Playlist> > diffed_playlists;
2939 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2940 i->view->thaw_after_trim ();
2941 i->view->enable_display (true);
2943 /* Trimming one region may affect others on the playlist, so we need
2944 to get undo Commands from the whole playlist rather than just the
2945 region. Use diffed_playlists to make sure we don't diff a given
2946 playlist more than once.
2948 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2949 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2950 vector<Command*> cmds;
2952 _editor->session()->add_commands (cmds);
2953 diffed_playlists.insert (p);
2958 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2962 _editor->motion_frozen_playlists.clear ();
2963 _editor->commit_reversible_command();
2966 /* no mouse movement */
2967 _editor->point_trim (event, adjusted_current_frame (event));
2970 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2971 if (_operation == StartTrim) {
2972 i->view->trim_front_ending ();
2975 i->view->region()->resume_property_changes ();
2980 TrimDrag::aborted (bool movement_occurred)
2982 /* Our motion method is changing model state, so use the Undo system
2983 to cancel. Perhaps not ideal, as this will leave an Undo point
2984 behind which may be slightly odd from the user's point of view.
2989 if (movement_occurred) {
2993 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2994 i->view->region()->resume_property_changes ();
2999 TrimDrag::setup_pointer_frame_offset ()
3001 list<DraggingView>::iterator i = _views.begin ();
3002 while (i != _views.end() && i->view != _primary) {
3006 if (i == _views.end()) {
3010 switch (_operation) {
3012 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3015 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3022 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3026 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3027 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3032 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3034 Drag::start_grab (event, cursor);
3035 show_verbose_cursor_time (adjusted_current_frame(event));
3039 MeterMarkerDrag::setup_pointer_frame_offset ()
3041 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3045 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3047 if (!_marker->meter().movable()) {
3053 // create a dummy marker for visual representation of moving the
3054 // section, because whether its a copy or not, we're going to
3055 // leave or lose the original marker (leave if its a copy; lose if its
3056 // not, because we'll remove it from the map).
3058 MeterSection section (_marker->meter());
3060 if (!section.movable()) {
3065 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3067 _marker = new MeterMarker (
3069 *_editor->meter_group,
3070 ARDOUR_UI::config()->color ("meter marker"),
3072 *new MeterSection (_marker->meter())
3075 /* use the new marker for the grab */
3076 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3079 TempoMap& map (_editor->session()->tempo_map());
3080 /* get current state */
3081 before_state = &map.get_state();
3082 /* remove the section while we drag it */
3083 map.remove_meter (section, true);
3087 framepos_t const pf = adjusted_current_frame (event);
3089 _marker->set_position (pf);
3090 show_verbose_cursor_time (pf);
3094 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3096 if (!movement_occurred) {
3097 if (was_double_click()) {
3098 _editor->edit_meter_marker (*_marker);
3103 if (!_marker->meter().movable()) {
3107 motion (event, false);
3109 Timecode::BBT_Time when;
3111 TempoMap& map (_editor->session()->tempo_map());
3112 map.bbt_time (last_pointer_frame(), when);
3114 if (_copy == true) {
3115 _editor->begin_reversible_command (_("copy meter mark"));
3116 XMLNode &before = map.get_state();
3117 map.add_meter (_marker->meter(), when);
3118 XMLNode &after = map.get_state();
3119 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3120 _editor->commit_reversible_command ();
3123 _editor->begin_reversible_command (_("move meter mark"));
3125 /* we removed it before, so add it back now */
3127 map.add_meter (_marker->meter(), when);
3128 XMLNode &after = map.get_state();
3129 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3130 _editor->commit_reversible_command ();
3133 // delete the dummy marker we used for visual representation while moving.
3134 // a new visual marker will show up automatically.
3139 MeterMarkerDrag::aborted (bool moved)
3141 _marker->set_position (_marker->meter().frame ());
3144 TempoMap& map (_editor->session()->tempo_map());
3145 /* we removed it before, so add it back now */
3146 map.add_meter (_marker->meter(), _marker->meter().frame());
3147 // delete the dummy marker we used for visual representation while moving.
3148 // a new visual marker will show up automatically.
3153 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3157 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3159 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3164 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3166 Drag::start_grab (event, cursor);
3167 show_verbose_cursor_time (adjusted_current_frame (event));
3171 TempoMarkerDrag::setup_pointer_frame_offset ()
3173 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3177 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3179 if (!_marker->tempo().movable()) {
3185 // create a dummy marker for visual representation of moving the
3186 // section, because whether its a copy or not, we're going to
3187 // leave or lose the original marker (leave if its a copy; lose if its
3188 // not, because we'll remove it from the map).
3190 // create a dummy marker for visual representation of moving the copy.
3191 // The actual copying is not done before we reach the finish callback.
3194 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3196 TempoSection section (_marker->tempo());
3198 _marker = new TempoMarker (
3200 *_editor->tempo_group,
3201 ARDOUR_UI::config()->color ("tempo marker"),
3203 *new TempoSection (_marker->tempo())
3206 /* use the new marker for the grab */
3207 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3210 TempoMap& map (_editor->session()->tempo_map());
3211 /* get current state */
3212 before_state = &map.get_state();
3213 /* remove the section while we drag it */
3214 map.remove_tempo (section, true);
3218 framepos_t const pf = adjusted_current_frame (event);
3219 _marker->set_position (pf);
3220 show_verbose_cursor_time (pf);
3224 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3226 if (!movement_occurred) {
3227 if (was_double_click()) {
3228 _editor->edit_tempo_marker (*_marker);
3233 if (!_marker->tempo().movable()) {
3237 motion (event, false);
3239 TempoMap& map (_editor->session()->tempo_map());
3240 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3241 Timecode::BBT_Time when;
3243 map.bbt_time (beat_time, when);
3245 if (_copy == true) {
3246 _editor->begin_reversible_command (_("copy tempo mark"));
3247 XMLNode &before = map.get_state();
3248 map.add_tempo (_marker->tempo(), when);
3249 XMLNode &after = map.get_state();
3250 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3251 _editor->commit_reversible_command ();
3254 _editor->begin_reversible_command (_("move tempo mark"));
3255 /* we removed it before, so add it back now */
3256 map.add_tempo (_marker->tempo(), when);
3257 XMLNode &after = map.get_state();
3258 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3259 _editor->commit_reversible_command ();
3262 // delete the dummy marker we used for visual representation while moving.
3263 // a new visual marker will show up automatically.
3268 TempoMarkerDrag::aborted (bool moved)
3270 _marker->set_position (_marker->tempo().frame());
3272 TempoMap& map (_editor->session()->tempo_map());
3273 /* we removed it before, so add it back now */
3274 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3275 // delete the dummy marker we used for visual representation while moving.
3276 // a new visual marker will show up automatically.
3281 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3282 : Drag (e, &c.track_canvas_item(), false)
3286 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3289 /** Do all the things we do when dragging the playhead to make it look as though
3290 * we have located, without actually doing the locate (because that would cause
3291 * the diskstream buffers to be refilled, which is too slow).
3294 CursorDrag::fake_locate (framepos_t t)
3296 _editor->playhead_cursor->set_position (t);
3298 Session* s = _editor->session ();
3299 if (s->timecode_transmission_suspended ()) {
3300 framepos_t const f = _editor->playhead_cursor->current_frame ();
3301 /* This is asynchronous so it will be sent "now"
3303 s->send_mmc_locate (f);
3304 /* These are synchronous and will be sent during the next
3307 s->queue_full_time_code ();
3308 s->queue_song_position_pointer ();
3311 show_verbose_cursor_time (t);
3312 _editor->UpdateAllTransportClocks (t);
3316 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3318 Drag::start_grab (event, c);
3319 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3321 _grab_zoom = _editor->samples_per_pixel;
3323 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3325 _editor->snap_to_with_modifier (where, event);
3327 _editor->_dragging_playhead = true;
3329 Session* s = _editor->session ();
3331 /* grab the track canvas item as well */
3333 _cursor.track_canvas_item().grab();
3336 if (_was_rolling && _stop) {
3340 if (s->is_auditioning()) {
3341 s->cancel_audition ();
3345 if (AudioEngine::instance()->connected()) {
3347 /* do this only if we're the engine is connected
3348 * because otherwise this request will never be
3349 * serviced and we'll busy wait forever. likewise,
3350 * notice if we are disconnected while waiting for the
3351 * request to be serviced.
3354 s->request_suspend_timecode_transmission ();
3355 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3356 /* twiddle our thumbs */
3361 fake_locate (where - snap_delta (event->button.state));
3365 CursorDrag::motion (GdkEvent* event, bool)
3367 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3368 _editor->snap_to_with_modifier (where, event);
3369 if (where != last_pointer_frame()) {
3370 fake_locate (where - snap_delta (event->button.state));
3375 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3377 _editor->_dragging_playhead = false;
3379 _cursor.track_canvas_item().ungrab();
3381 if (!movement_occurred && _stop) {
3385 motion (event, false);
3387 Session* s = _editor->session ();
3389 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3390 _editor->_pending_locate_request = true;
3391 s->request_resume_timecode_transmission ();
3396 CursorDrag::aborted (bool)
3398 _cursor.track_canvas_item().ungrab();
3400 if (_editor->_dragging_playhead) {
3401 _editor->session()->request_resume_timecode_transmission ();
3402 _editor->_dragging_playhead = false;
3405 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3408 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3409 : RegionDrag (e, i, p, v)
3411 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3415 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3417 Drag::start_grab (event, cursor);
3419 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3420 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3421 setup_snap_delta (r->position ());
3423 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3427 FadeInDrag::setup_pointer_frame_offset ()
3429 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3430 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3431 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3435 FadeInDrag::motion (GdkEvent* event, bool)
3437 framecnt_t fade_length;
3439 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3440 _editor->snap_to_with_modifier (pos, event);
3441 pos -= snap_delta (event->button.state);
3443 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3445 if (pos < (region->position() + 64)) {
3446 fade_length = 64; // this should be a minimum defined somewhere
3447 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3448 fade_length = region->length() - region->fade_out()->back()->when - 1;
3450 fade_length = pos - region->position();
3453 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3455 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3461 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3464 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3468 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3470 if (!movement_occurred) {
3474 framecnt_t fade_length;
3475 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3476 _editor->snap_to_with_modifier (pos, event);
3477 pos -= snap_delta (event->button.state);
3479 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3481 if (pos < (region->position() + 64)) {
3482 fade_length = 64; // this should be a minimum defined somewhere
3483 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3484 fade_length = region->length() - region->fade_out()->back()->when - 1;
3486 fade_length = pos - region->position();
3489 bool in_command = false;
3491 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3493 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3499 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3500 XMLNode &before = alist->get_state();
3502 tmp->audio_region()->set_fade_in_length (fade_length);
3503 tmp->audio_region()->set_fade_in_active (true);
3506 _editor->begin_reversible_command (_("change fade in length"));
3509 XMLNode &after = alist->get_state();
3510 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3514 _editor->commit_reversible_command ();
3519 FadeInDrag::aborted (bool)
3521 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3522 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3528 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3532 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3533 : RegionDrag (e, i, p, v)
3535 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3539 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3541 Drag::start_grab (event, cursor);
3543 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3544 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3545 setup_snap_delta (r->last_frame ());
3547 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3551 FadeOutDrag::setup_pointer_frame_offset ()
3553 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3554 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3555 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3559 FadeOutDrag::motion (GdkEvent* event, bool)
3561 framecnt_t fade_length;
3563 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3564 _editor->snap_to_with_modifier (pos, event);
3565 pos -= snap_delta (event->button.state);
3567 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3569 if (pos > (region->last_frame() - 64)) {
3570 fade_length = 64; // this should really be a minimum fade defined somewhere
3571 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3572 fade_length = region->length() - region->fade_in()->back()->when - 1;
3574 fade_length = region->last_frame() - pos;
3577 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3579 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3585 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3588 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3592 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3594 if (!movement_occurred) {
3598 framecnt_t fade_length;
3600 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3601 _editor->snap_to_with_modifier (pos, event);
3602 pos -= snap_delta (event->button.state);
3604 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3606 if (pos > (region->last_frame() - 64)) {
3607 fade_length = 64; // this should really be a minimum fade defined somewhere
3608 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3609 fade_length = region->length() - region->fade_in()->back()->when - 1;
3611 fade_length = region->last_frame() - pos;
3614 bool in_command = false;
3616 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3618 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3624 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3625 XMLNode &before = alist->get_state();
3627 tmp->audio_region()->set_fade_out_length (fade_length);
3628 tmp->audio_region()->set_fade_out_active (true);
3631 _editor->begin_reversible_command (_("change fade out length"));
3634 XMLNode &after = alist->get_state();
3635 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3639 _editor->commit_reversible_command ();
3644 FadeOutDrag::aborted (bool)
3646 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3647 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3653 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3657 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3660 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3662 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3665 _points.push_back (ArdourCanvas::Duple (0, 0));
3666 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3669 MarkerDrag::~MarkerDrag ()
3671 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3676 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3678 location = new Location (*l);
3679 markers.push_back (m);
3684 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3686 Drag::start_grab (event, cursor);
3690 Location *location = _editor->find_location_from_marker (_marker, is_start);
3691 _editor->_dragging_edit_point = true;
3693 update_item (location);
3695 // _drag_line->show();
3696 // _line->raise_to_top();
3699 show_verbose_cursor_time (location->start());
3701 show_verbose_cursor_time (location->end());
3704 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3707 case Selection::Toggle:
3708 /* we toggle on the button release */
3710 case Selection::Set:
3711 if (!_editor->selection->selected (_marker)) {
3712 _editor->selection->set (_marker);
3715 case Selection::Extend:
3717 Locations::LocationList ll;
3718 list<Marker*> to_add;
3720 _editor->selection->markers.range (s, e);
3721 s = min (_marker->position(), s);
3722 e = max (_marker->position(), e);
3725 if (e < max_framepos) {
3728 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3729 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3730 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3733 to_add.push_back (lm->start);
3736 to_add.push_back (lm->end);
3740 if (!to_add.empty()) {
3741 _editor->selection->add (to_add);
3745 case Selection::Add:
3746 _editor->selection->add (_marker);
3750 /* Set up copies for us to manipulate during the drag
3753 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3755 Location* l = _editor->find_location_from_marker (*i, is_start);
3762 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3764 /* range: check that the other end of the range isn't
3767 CopiedLocationInfo::iterator x;
3768 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3769 if (*(*x).location == *l) {
3773 if (x == _copied_locations.end()) {
3774 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3776 (*x).markers.push_back (*i);
3777 (*x).move_both = true;
3785 MarkerDrag::setup_pointer_frame_offset ()
3788 Location *location = _editor->find_location_from_marker (_marker, is_start);
3789 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3793 MarkerDrag::motion (GdkEvent* event, bool)
3795 framecnt_t f_delta = 0;
3797 bool move_both = false;
3798 Location *real_location;
3799 Location *copy_location = 0;
3801 framepos_t const newframe = adjusted_current_frame (event);
3802 framepos_t next = newframe;
3804 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
3808 CopiedLocationInfo::iterator x;
3810 /* find the marker we're dragging, and compute the delta */
3812 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3814 copy_location = (*x).location;
3816 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3818 /* this marker is represented by this
3819 * CopiedLocationMarkerInfo
3822 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3827 if (real_location->is_mark()) {
3828 f_delta = newframe - copy_location->start();
3832 switch (_marker->type()) {
3833 case Marker::SessionStart:
3834 case Marker::RangeStart:
3835 case Marker::LoopStart:
3836 case Marker::PunchIn:
3837 f_delta = newframe - copy_location->start();
3840 case Marker::SessionEnd:
3841 case Marker::RangeEnd:
3842 case Marker::LoopEnd:
3843 case Marker::PunchOut:
3844 f_delta = newframe - copy_location->end();
3847 /* what kind of marker is this ? */
3856 if (x == _copied_locations.end()) {
3857 /* hmm, impossible - we didn't find the dragged marker */
3861 /* now move them all */
3863 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3865 copy_location = x->location;
3867 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3871 if (real_location->locked()) {
3875 if (copy_location->is_mark()) {
3879 copy_location->set_start (copy_location->start() + f_delta);
3883 framepos_t new_start = copy_location->start() + f_delta;
3884 framepos_t new_end = copy_location->end() + f_delta;
3886 if (is_start) { // start-of-range marker
3888 if (move_both || (*x).move_both) {
3889 copy_location->set_start (new_start);
3890 copy_location->set_end (new_end);
3891 } else if (new_start < copy_location->end()) {
3892 copy_location->set_start (new_start);
3893 } else if (newframe > 0) {
3894 _editor->snap_to (next, RoundUpAlways, true);
3895 copy_location->set_end (next);
3896 copy_location->set_start (newframe);
3899 } else { // end marker
3901 if (move_both || (*x).move_both) {
3902 copy_location->set_end (new_end);
3903 copy_location->set_start (new_start);
3904 } else if (new_end > copy_location->start()) {
3905 copy_location->set_end (new_end);
3906 } else if (newframe > 0) {
3907 _editor->snap_to (next, RoundDownAlways, true);
3908 copy_location->set_start (next);
3909 copy_location->set_end (newframe);
3914 update_item (copy_location);
3916 /* now lookup the actual GUI items used to display this
3917 * location and move them to wherever the copy of the location
3918 * is now. This means that the logic in ARDOUR::Location is
3919 * still enforced, even though we are not (yet) modifying
3920 * the real Location itself.
3923 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3926 lm->set_position (copy_location->start(), copy_location->end());
3931 assert (!_copied_locations.empty());
3933 show_verbose_cursor_time (newframe);
3937 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3939 if (!movement_occurred) {
3941 if (was_double_click()) {
3942 _editor->rename_marker (_marker);
3946 /* just a click, do nothing but finish
3947 off the selection process
3950 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3953 case Selection::Set:
3954 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3955 _editor->selection->set (_marker);
3959 case Selection::Toggle:
3960 /* we toggle on the button release, click only */
3961 _editor->selection->toggle (_marker);
3964 case Selection::Extend:
3965 case Selection::Add:
3972 _editor->_dragging_edit_point = false;
3974 XMLNode &before = _editor->session()->locations()->get_state();
3975 bool in_command = false;
3977 MarkerSelection::iterator i;
3978 CopiedLocationInfo::iterator x;
3981 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3982 x != _copied_locations.end() && i != _editor->selection->markers.end();
3985 Location * location = _editor->find_location_from_marker (*i, is_start);
3989 if (location->locked()) {
3993 _editor->begin_reversible_command ( _("move marker") );
3996 if (location->is_mark()) {
3997 location->set_start (((*x).location)->start());
3999 location->set (((*x).location)->start(), ((*x).location)->end());
4005 XMLNode &after = _editor->session()->locations()->get_state();
4006 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4007 _editor->commit_reversible_command ();
4012 MarkerDrag::aborted (bool movement_occured)
4014 if (!movement_occured) {
4018 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4020 /* move all markers to their original location */
4023 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4026 Location * location = _editor->find_location_from_marker (*m, is_start);
4029 (*m)->set_position (is_start ? location->start() : location->end());
4036 MarkerDrag::update_item (Location*)
4041 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4043 _cumulative_x_drag (0),
4044 _cumulative_y_drag (0)
4046 if (_zero_gain_fraction < 0.0) {
4047 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4050 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4052 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4058 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4060 Drag::start_grab (event, _editor->cursors()->fader);
4062 // start the grab at the center of the control point so
4063 // the point doesn't 'jump' to the mouse after the first drag
4064 _fixed_grab_x = _point->get_x();
4065 _fixed_grab_y = _point->get_y();
4067 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4068 setup_snap_delta (pos);
4070 float const fraction = 1 - (_point->get_y() / _point->line().height());
4071 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4073 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4075 if (!_point->can_slide ()) {
4076 _x_constrained = true;
4081 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4083 double dx = _drags->current_pointer_x() - last_pointer_x();
4084 double dy = current_pointer_y() - last_pointer_y();
4086 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4091 /* coordinate in pixels relative to the start of the region (for region-based automation)
4092 or track (for track-based automation) */
4093 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4094 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4096 // calculate zero crossing point. back off by .01 to stay on the
4097 // positive side of zero
4098 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4100 if (_x_constrained) {
4103 if (_y_constrained) {
4107 _cumulative_x_drag = cx - _fixed_grab_x;
4108 _cumulative_y_drag = cy - _fixed_grab_y;
4110 // make sure we hit zero when passing through
4111 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4117 cy = min ((double) _point->line().height(), cy);
4119 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4121 if (!_x_constrained) {
4122 _editor->snap_to_with_modifier (cx_frames, event);
4125 cx_frames -= snap_delta (event->button.state);
4126 cx_frames = min (cx_frames, _point->line().maximum_time());
4128 float const fraction = 1.0 - (cy / _point->line().height());
4131 _editor->begin_reversible_command (_("automation event move"));
4132 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
4135 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4137 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4141 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4143 if (!movement_occurred) {
4146 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4147 _editor->reset_point_selection ();
4151 motion (event, false);
4152 _point->line().end_drag (_pushing, _final_index);
4153 _editor->commit_reversible_command ();
4158 ControlPointDrag::aborted (bool)
4160 _point->line().reset ();
4164 ControlPointDrag::active (Editing::MouseMode m)
4166 if (m == Editing::MouseDraw) {
4167 /* always active in mouse draw */
4171 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4172 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4175 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4178 , _cumulative_y_drag (0)
4182 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4186 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4188 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4191 _item = &_line->grab_item ();
4193 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4194 origin, and ditto for y.
4197 double cx = event->button.x;
4198 double cy = event->button.y;
4200 _line->parent_group().canvas_to_item (cx, cy);
4202 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4204 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4205 /* no adjacent points */
4209 Drag::start_grab (event, _editor->cursors()->fader);
4211 /* store grab start in parent frame */
4216 double fraction = 1.0 - (cy / _line->height());
4218 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4222 LineDrag::motion (GdkEvent* event, bool first_move)
4224 double dy = current_pointer_y() - last_pointer_y();
4226 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4230 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4232 _cumulative_y_drag = cy - _fixed_grab_y;
4235 cy = min ((double) _line->height(), cy);
4237 double const fraction = 1.0 - (cy / _line->height());
4241 _editor->begin_reversible_command (_("automation range move"));
4242 _line->start_drag_line (_before, _after, fraction);
4245 /* we are ignoring x position for this drag, so we can just pass in anything */
4246 _line->drag_motion (0, fraction, true, false, ignored);
4248 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4252 LineDrag::finished (GdkEvent* event, bool movement_occured)
4254 if (movement_occured) {
4255 motion (event, false);
4256 _line->end_drag (false, 0);
4257 _editor->commit_reversible_command ();
4259 /* add a new control point on the line */
4261 AutomationTimeAxisView* atv;
4263 _line->end_drag (false, 0);
4265 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4266 framepos_t where = _editor->window_event_sample (event, 0, 0);
4267 atv->add_automation_event (event, where, event->button.y, false);
4273 LineDrag::aborted (bool)
4278 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4281 _cumulative_x_drag (0)
4283 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4287 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4289 Drag::start_grab (event);
4291 _line = reinterpret_cast<Line*> (_item);
4294 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4296 double cx = event->button.x;
4297 double cy = event->button.y;
4299 _item->parent()->canvas_to_item (cx, cy);
4301 /* store grab start in parent frame */
4302 _region_view_grab_x = cx;
4304 _before = *(float*) _item->get_data ("position");
4306 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4308 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4312 FeatureLineDrag::motion (GdkEvent*, bool)
4314 double dx = _drags->current_pointer_x() - last_pointer_x();
4316 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4318 _cumulative_x_drag += dx;
4320 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4329 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4331 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4333 float *pos = new float;
4336 _line->set_data ("position", pos);
4342 FeatureLineDrag::finished (GdkEvent*, bool)
4344 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4345 _arv->update_transient(_before, _before);
4349 FeatureLineDrag::aborted (bool)
4354 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4356 , _vertical_only (false)
4358 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4362 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4364 Drag::start_grab (event);
4365 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4369 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4376 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4378 framepos_t grab = grab_frame ();
4379 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4380 _editor->snap_to_with_modifier (grab, event);
4382 grab = raw_grab_frame ();
4385 /* base start and end on initial click position */
4395 if (current_pointer_y() < grab_y()) {
4396 y1 = current_pointer_y();
4399 y2 = current_pointer_y();
4403 if (start != end || y1 != y2) {
4405 double x1 = _editor->sample_to_pixel (start);
4406 double x2 = _editor->sample_to_pixel (end);
4407 const double min_dimension = 2.0;
4409 if (_vertical_only) {
4410 /* fixed 10 pixel width */
4414 x2 = min (x1 - min_dimension, x2);
4416 x2 = max (x1 + min_dimension, x2);
4421 y2 = min (y1 - min_dimension, y2);
4423 y2 = max (y1 + min_dimension, y2);
4426 /* translate rect into item space and set */
4428 ArdourCanvas::Rect r (x1, y1, x2, y2);
4430 /* this drag is a _trackview_only == true drag, so the y1 and
4431 * y2 (computed using current_pointer_y() and grab_y()) will be
4432 * relative to the top of the trackview group). The
4433 * rubberband rect has the same parent/scroll offset as the
4434 * the trackview group, so we can use the "r" rect directly
4435 * to set the shape of the rubberband.
4438 _editor->rubberband_rect->set (r);
4439 _editor->rubberband_rect->show();
4440 _editor->rubberband_rect->raise_to_top();
4442 show_verbose_cursor_time (pf);
4444 do_select_things (event, true);
4449 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4453 framepos_t grab = grab_frame ();
4454 framepos_t lpf = last_pointer_frame ();
4456 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4457 grab = raw_grab_frame ();
4458 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4472 if (current_pointer_y() < grab_y()) {
4473 y1 = current_pointer_y();
4476 y2 = current_pointer_y();
4480 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4484 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4486 if (movement_occurred) {
4488 motion (event, false);
4489 do_select_things (event, false);
4495 bool do_deselect = true;
4496 MidiTimeAxisView* mtv;
4498 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4500 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4501 /* nothing selected */
4502 add_midi_region (mtv);
4503 do_deselect = false;
4507 /* do not deselect if Primary or Tertiary (toggle-select or
4508 * extend-select are pressed.
4511 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4512 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4519 _editor->rubberband_rect->hide();
4523 RubberbandSelectDrag::aborted (bool)
4525 _editor->rubberband_rect->hide ();
4528 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4529 : RegionDrag (e, i, p, v)
4531 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4535 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4537 Drag::start_grab (event, cursor);
4539 _editor->get_selection().add (_primary);
4541 framepos_t where = _primary->region()->position();
4542 setup_snap_delta (where);
4544 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4548 TimeFXDrag::motion (GdkEvent* event, bool)
4550 RegionView* rv = _primary;
4551 StreamView* cv = rv->get_time_axis_view().view ();
4553 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4554 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4555 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4556 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4557 _editor->snap_to_with_modifier (pf, event);
4558 pf -= snap_delta (event->button.state);
4560 if (pf > rv->region()->position()) {
4561 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4564 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4568 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4570 _primary->get_time_axis_view().hide_timestretch ();
4572 if (!movement_occurred) {
4576 if (last_pointer_frame() < _primary->region()->position()) {
4577 /* backwards drag of the left edge - not usable */
4581 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4583 float percentage = (double) newlen / (double) _primary->region()->length();
4585 #ifndef USE_RUBBERBAND
4586 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4587 if (_primary->region()->data_type() == DataType::AUDIO) {
4588 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4592 if (!_editor->get_selection().regions.empty()) {
4593 /* primary will already be included in the selection, and edit
4594 group shared editing will propagate selection across
4595 equivalent regions, so just use the current region
4599 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4600 error << _("An error occurred while executing time stretch operation") << endmsg;
4606 TimeFXDrag::aborted (bool)
4608 _primary->get_time_axis_view().hide_timestretch ();
4611 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4614 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4618 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4620 Drag::start_grab (event);
4624 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4626 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4630 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4632 if (movement_occurred && _editor->session()) {
4633 /* make sure we stop */
4634 _editor->session()->request_transport_speed (0.0);
4639 ScrubDrag::aborted (bool)
4644 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4648 , _time_selection_at_start (!_editor->get_selection().time.empty())
4650 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4652 if (_time_selection_at_start) {
4653 start_at_start = _editor->get_selection().time.start();
4654 end_at_start = _editor->get_selection().time.end_frame();
4659 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4661 if (_editor->session() == 0) {
4665 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4667 switch (_operation) {
4668 case CreateSelection:
4669 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4674 cursor = _editor->cursors()->selector;
4675 Drag::start_grab (event, cursor);
4678 case SelectionStartTrim:
4679 if (_editor->clicked_axisview) {
4680 _editor->clicked_axisview->order_selection_trims (_item, true);
4682 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4685 case SelectionEndTrim:
4686 if (_editor->clicked_axisview) {
4687 _editor->clicked_axisview->order_selection_trims (_item, false);
4689 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4693 Drag::start_grab (event, cursor);
4696 case SelectionExtend:
4697 Drag::start_grab (event, cursor);
4701 if (_operation == SelectionMove) {
4702 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4704 show_verbose_cursor_time (adjusted_current_frame (event));
4709 SelectionDrag::setup_pointer_frame_offset ()
4711 switch (_operation) {
4712 case CreateSelection:
4713 _pointer_frame_offset = 0;
4716 case SelectionStartTrim:
4718 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4721 case SelectionEndTrim:
4722 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4725 case SelectionExtend:
4731 SelectionDrag::motion (GdkEvent* event, bool first_move)
4733 framepos_t start = 0;
4735 framecnt_t length = 0;
4736 framecnt_t distance = 0;
4738 framepos_t const pending_position = adjusted_current_frame (event);
4740 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4744 switch (_operation) {
4745 case CreateSelection:
4747 framepos_t grab = grab_frame ();
4750 grab = adjusted_current_frame (event, false);
4751 if (grab < pending_position) {
4752 _editor->snap_to (grab, RoundDownMaybe);
4754 _editor->snap_to (grab, RoundUpMaybe);
4758 if (pending_position < grab) {
4759 start = pending_position;
4762 end = pending_position;
4766 /* first drag: Either add to the selection
4767 or create a new selection
4774 /* adding to the selection */
4775 _editor->set_selected_track_as_side_effect (Selection::Add);
4776 _editor->clicked_selection = _editor->selection->add (start, end);
4783 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4784 _editor->set_selected_track_as_side_effect (Selection::Set);
4787 _editor->clicked_selection = _editor->selection->set (start, end);
4791 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4792 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4793 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4795 _editor->selection->add (atest);
4799 /* select all tracks within the rectangle that we've marked out so far */
4800 TrackViewList new_selection;
4801 TrackViewList& all_tracks (_editor->track_views);
4803 ArdourCanvas::Coord const top = grab_y();
4804 ArdourCanvas::Coord const bottom = current_pointer_y();
4806 if (top >= 0 && bottom >= 0) {
4808 //first, find the tracks that are covered in the y range selection
4809 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4810 if ((*i)->covered_by_y_range (top, bottom)) {
4811 new_selection.push_back (*i);
4815 //now find any tracks that are GROUPED with the tracks we selected
4816 TrackViewList grouped_add = new_selection;
4817 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4818 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4819 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4820 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4821 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4822 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4823 grouped_add.push_back (*j);
4828 //now compare our list with the current selection, and add or remove as necessary
4829 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4830 TrackViewList tracks_to_add;
4831 TrackViewList tracks_to_remove;
4832 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4833 if ( !_editor->selection->tracks.contains ( *i ) )
4834 tracks_to_add.push_back ( *i );
4835 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4836 if ( !grouped_add.contains ( *i ) )
4837 tracks_to_remove.push_back ( *i );
4838 _editor->selection->add(tracks_to_add);
4839 _editor->selection->remove(tracks_to_remove);
4845 case SelectionStartTrim:
4847 start = _editor->selection->time[_editor->clicked_selection].start;
4848 end = _editor->selection->time[_editor->clicked_selection].end;
4850 if (pending_position > end) {
4853 start = pending_position;
4857 case SelectionEndTrim:
4859 start = _editor->selection->time[_editor->clicked_selection].start;
4860 end = _editor->selection->time[_editor->clicked_selection].end;
4862 if (pending_position < start) {
4865 end = pending_position;
4872 start = _editor->selection->time[_editor->clicked_selection].start;
4873 end = _editor->selection->time[_editor->clicked_selection].end;
4875 length = end - start;
4876 distance = pending_position - start;
4877 start = pending_position;
4878 _editor->snap_to (start);
4880 end = start + length;
4884 case SelectionExtend:
4889 switch (_operation) {
4891 if (_time_selection_at_start) {
4892 _editor->selection->move_time (distance);
4896 _editor->selection->replace (_editor->clicked_selection, start, end);
4900 if (_operation == SelectionMove) {
4901 show_verbose_cursor_time(start);
4903 show_verbose_cursor_time(pending_position);
4908 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4910 Session* s = _editor->session();
4912 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4913 if (movement_occurred) {
4914 motion (event, false);
4915 /* XXX this is not object-oriented programming at all. ick */
4916 if (_editor->selection->time.consolidate()) {
4917 _editor->selection->TimeChanged ();
4920 /* XXX what if its a music time selection? */
4922 if ( s->get_play_range() && s->transport_rolling() ) {
4923 s->request_play_range (&_editor->selection->time, true);
4925 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4926 if (_operation == SelectionEndTrim)
4927 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4929 s->request_locate (_editor->get_selection().time.start());
4935 /* just a click, no pointer movement.
4938 if (_operation == SelectionExtend) {
4939 if (_time_selection_at_start) {
4940 framepos_t pos = adjusted_current_frame (event, false);
4941 framepos_t start = min (pos, start_at_start);
4942 framepos_t end = max (pos, end_at_start);
4943 _editor->selection->set (start, end);
4946 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4947 if (_editor->clicked_selection) {
4948 _editor->selection->remove (_editor->clicked_selection);
4951 if (!_editor->clicked_selection) {
4952 _editor->selection->clear_time();
4957 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4958 _editor->selection->set (_editor->clicked_axisview);
4961 if (s && s->get_play_range () && s->transport_rolling()) {
4962 s->request_stop (false, false);
4967 _editor->stop_canvas_autoscroll ();
4968 _editor->clicked_selection = 0;
4969 _editor->commit_reversible_selection_op ();
4973 SelectionDrag::aborted (bool)
4978 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4979 : Drag (e, i, false),
4983 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4985 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4986 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4987 physical_screen_height (_editor->get_window())));
4988 _drag_rect->hide ();
4990 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4991 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4994 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4996 /* normal canvas items will be cleaned up when their parent group is deleted. But
4997 this item is created as the child of a long-lived parent group, and so we
4998 need to explicitly delete it.
5004 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5006 if (_editor->session() == 0) {
5010 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5012 if (!_editor->temp_location) {
5013 _editor->temp_location = new Location (*_editor->session());
5016 switch (_operation) {
5017 case CreateSkipMarker:
5018 case CreateRangeMarker:
5019 case CreateTransportMarker:
5020 case CreateCDMarker:
5022 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5027 cursor = _editor->cursors()->selector;
5031 Drag::start_grab (event, cursor);
5033 show_verbose_cursor_time (adjusted_current_frame (event));
5037 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5039 framepos_t start = 0;
5041 ArdourCanvas::Rectangle *crect;
5043 switch (_operation) {
5044 case CreateSkipMarker:
5045 crect = _editor->range_bar_drag_rect;
5047 case CreateRangeMarker:
5048 crect = _editor->range_bar_drag_rect;
5050 case CreateTransportMarker:
5051 crect = _editor->transport_bar_drag_rect;
5053 case CreateCDMarker:
5054 crect = _editor->cd_marker_bar_drag_rect;
5057 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5062 framepos_t const pf = adjusted_current_frame (event);
5064 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5065 framepos_t grab = grab_frame ();
5066 _editor->snap_to (grab);
5068 if (pf < grab_frame()) {
5076 /* first drag: Either add to the selection
5077 or create a new selection.
5082 _editor->temp_location->set (start, end);
5086 update_item (_editor->temp_location);
5088 //_drag_rect->raise_to_top();
5094 _editor->temp_location->set (start, end);
5096 double x1 = _editor->sample_to_pixel (start);
5097 double x2 = _editor->sample_to_pixel (end);
5101 update_item (_editor->temp_location);
5104 show_verbose_cursor_time (pf);
5109 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5111 Location * newloc = 0;
5115 if (movement_occurred) {
5116 motion (event, false);
5119 switch (_operation) {
5120 case CreateSkipMarker:
5121 case CreateRangeMarker:
5122 case CreateCDMarker:
5124 XMLNode &before = _editor->session()->locations()->get_state();
5125 if (_operation == CreateSkipMarker) {
5126 _editor->begin_reversible_command (_("new skip marker"));
5127 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5128 flags = Location::IsRangeMarker | Location::IsSkip;
5129 _editor->range_bar_drag_rect->hide();
5130 } else if (_operation == CreateCDMarker) {
5131 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5132 _editor->begin_reversible_command (_("new CD marker"));
5133 flags = Location::IsRangeMarker | Location::IsCDMarker;
5134 _editor->cd_marker_bar_drag_rect->hide();
5136 _editor->begin_reversible_command (_("new skip marker"));
5137 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5138 flags = Location::IsRangeMarker;
5139 _editor->range_bar_drag_rect->hide();
5141 newloc = new Location (
5142 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5145 _editor->session()->locations()->add (newloc, true);
5146 XMLNode &after = _editor->session()->locations()->get_state();
5147 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5148 _editor->commit_reversible_command ();
5152 case CreateTransportMarker:
5153 // popup menu to pick loop or punch
5154 _editor->new_transport_marker_context_menu (&event->button, _item);
5160 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5162 if (_operation == CreateTransportMarker) {
5164 /* didn't drag, so just locate */
5166 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5168 } else if (_operation == CreateCDMarker) {
5170 /* didn't drag, but mark is already created so do
5173 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5178 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5180 if (end == max_framepos) {
5181 end = _editor->session()->current_end_frame ();
5184 if (start == max_framepos) {
5185 start = _editor->session()->current_start_frame ();
5188 switch (_editor->mouse_mode) {
5190 /* find the two markers on either side and then make the selection from it */
5191 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5195 /* find the two markers on either side of the click and make the range out of it */
5196 _editor->selection->set (start, end);
5205 _editor->stop_canvas_autoscroll ();
5209 RangeMarkerBarDrag::aborted (bool movement_occured)
5211 if (movement_occured) {
5212 _drag_rect->hide ();
5217 RangeMarkerBarDrag::update_item (Location* location)
5219 double const x1 = _editor->sample_to_pixel (location->start());
5220 double const x2 = _editor->sample_to_pixel (location->end());
5222 _drag_rect->set_x0 (x1);
5223 _drag_rect->set_x1 (x2);
5226 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5228 , _cumulative_dx (0)
5229 , _cumulative_dy (0)
5231 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5233 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5235 _region = &_primary->region_view ();
5236 _note_height = _region->midi_stream_view()->note_height ();
5240 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5242 Drag::start_grab (event);
5243 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5245 if (!(_was_selected = _primary->selected())) {
5247 /* tertiary-click means extend selection - we'll do that on button release,
5248 so don't add it here, because otherwise we make it hard to figure
5249 out the "extend-to" range.
5252 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5255 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5258 _region->note_selected (_primary, true);
5260 _region->unique_select (_primary);
5263 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5264 _editor->commit_reversible_selection_op();
5269 /** @return Current total drag x change in frames */
5271 NoteDrag::total_dx (const guint state) const
5274 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5276 /* primary note time */
5277 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5279 /* new time of the primary note in session frames */
5280 frameoffset_t st = n + dx + snap_delta (state);
5282 framepos_t const rp = _region->region()->position ();
5284 /* prevent the note being dragged earlier than the region's position */
5287 /* possibly snap and return corresponding delta */
5291 if (ArdourKeyboard::indicates_snap (state)) {
5292 if (_editor->snap_mode () != SnapOff) {
5296 if (_editor->snap_mode () == SnapOff) {
5298 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5299 if (ArdourKeyboard::indicates_snap_delta (state)) {
5307 ret = _region->snap_frame_to_frame (st - rp) + rp - n - snap_delta (state);
5309 ret = st - n - snap_delta (state);
5314 /** @return Current total drag y change in note number */
5316 NoteDrag::total_dy () const
5318 MidiStreamView* msv = _region->midi_stream_view ();
5319 double const y = _region->midi_view()->y_position ();
5320 /* new current note */
5321 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5323 n = max (msv->lowest_note(), n);
5324 n = min (msv->highest_note(), n);
5325 /* and work out delta */
5326 return n - msv->y_to_note (grab_y() - y);
5330 NoteDrag::motion (GdkEvent * event, bool)
5332 /* Total change in x and y since the start of the drag */
5333 frameoffset_t const dx = total_dx (event->button.state);
5334 int8_t const dy = total_dy ();
5336 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5337 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5338 double const tdy = -dy * _note_height - _cumulative_dy;
5341 _cumulative_dx += tdx;
5342 _cumulative_dy += tdy;
5344 int8_t note_delta = total_dy();
5346 _region->move_selection (tdx, tdy, note_delta);
5348 /* the new note value may be the same as the old one, but we
5349 * don't know what that means because the selection may have
5350 * involved more than one note and we might be doing something
5351 * odd with them. so show the note value anyway, always.
5355 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5357 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5358 (int) floor ((double)new_note));
5360 show_verbose_cursor_text (buf);
5365 NoteDrag::finished (GdkEvent* ev, bool moved)
5368 /* no motion - select note */
5370 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5371 _editor->current_mouse_mode() == Editing::MouseDraw) {
5373 bool changed = false;
5375 if (_was_selected) {
5376 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5378 _region->note_deselected (_primary);
5382 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5383 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5385 if (!extend && !add && _region->selection_size() > 1) {
5386 _region->unique_select (_primary);
5388 } else if (extend) {
5389 _region->note_selected (_primary, true, true);
5392 /* it was added during button press */
5397 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5398 _editor->commit_reversible_selection_op();
5402 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5407 NoteDrag::aborted (bool)
5412 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5413 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5414 : Drag (editor, atv->base_item ())
5416 , _y_origin (atv->y_position())
5417 , _nothing_to_drag (false)
5419 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5420 setup (atv->lines ());
5423 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5424 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5425 : Drag (editor, rv->get_canvas_group ())
5427 , _y_origin (rv->get_time_axis_view().y_position())
5428 , _nothing_to_drag (false)
5431 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5433 list<boost::shared_ptr<AutomationLine> > lines;
5435 AudioRegionView* audio_view;
5436 AutomationRegionView* automation_view;
5437 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5438 lines.push_back (audio_view->get_gain_line ());
5439 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5440 lines.push_back (automation_view->line ());
5443 error << _("Automation range drag created for invalid region type") << endmsg;
5449 /** @param lines AutomationLines to drag.
5450 * @param offset Offset from the session start to the points in the AutomationLines.
5453 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5455 /* find the lines that overlap the ranges being dragged */
5456 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5457 while (i != lines.end ()) {
5458 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5461 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5463 /* check this range against all the AudioRanges that we are using */
5464 list<AudioRange>::const_iterator k = _ranges.begin ();
5465 while (k != _ranges.end()) {
5466 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5472 /* add it to our list if it overlaps at all */
5473 if (k != _ranges.end()) {
5478 _lines.push_back (n);
5484 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5488 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5490 return 1.0 - ((global_y - _y_origin) / line->height());
5494 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5496 const double v = list->eval(x);
5497 return _integral ? rint(v) : v;
5501 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5503 Drag::start_grab (event, cursor);
5505 /* Get line states before we start changing things */
5506 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5507 i->state = &i->line->get_state ();
5508 i->original_fraction = y_fraction (i->line, current_pointer_y());
5511 if (_ranges.empty()) {
5513 /* No selected time ranges: drag all points */
5514 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5515 uint32_t const N = i->line->npoints ();
5516 for (uint32_t j = 0; j < N; ++j) {
5517 i->points.push_back (i->line->nth (j));
5523 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5525 framecnt_t const half = (i->start + i->end) / 2;
5527 /* find the line that this audio range starts in */
5528 list<Line>::iterator j = _lines.begin();
5529 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5533 if (j != _lines.end()) {
5534 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5536 /* j is the line that this audio range starts in; fade into it;
5537 64 samples length plucked out of thin air.
5540 framepos_t a = i->start + 64;
5545 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5546 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5548 the_list->editor_add (p, value (the_list, p), false);
5549 the_list->editor_add (q, value (the_list, q), false);
5552 /* same thing for the end */
5555 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5559 if (j != _lines.end()) {
5560 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5562 /* j is the line that this audio range starts in; fade out of it;
5563 64 samples length plucked out of thin air.
5566 framepos_t b = i->end - 64;
5571 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5572 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5574 the_list->editor_add (p, value (the_list, p), false);
5575 the_list->editor_add (q, value (the_list, q), false);
5579 _nothing_to_drag = true;
5581 /* Find all the points that should be dragged and put them in the relevant
5582 points lists in the Line structs.
5585 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5587 uint32_t const N = i->line->npoints ();
5588 for (uint32_t j = 0; j < N; ++j) {
5590 /* here's a control point on this line */
5591 ControlPoint* p = i->line->nth (j);
5592 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5594 /* see if it's inside a range */
5595 list<AudioRange>::const_iterator k = _ranges.begin ();
5596 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5600 if (k != _ranges.end()) {
5601 /* dragging this point */
5602 _nothing_to_drag = false;
5603 i->points.push_back (p);
5609 if (_nothing_to_drag) {
5615 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5617 if (_nothing_to_drag) {
5622 _editor->begin_reversible_command (_("automation range move"));
5623 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5624 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5628 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5629 float const f = y_fraction (l->line, current_pointer_y());
5630 /* we are ignoring x position for this drag, so we can just pass in anything */
5632 l->line->drag_motion (0, f, true, false, ignored);
5633 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5638 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5640 if (_nothing_to_drag || !motion_occurred) {
5644 motion (event, false);
5645 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5646 i->line->end_drag (false, 0);
5649 _editor->commit_reversible_command ();
5653 AutomationRangeDrag::aborted (bool)
5655 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5660 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5662 , initial_time_axis_view (itav)
5664 /* note that time_axis_view may be null if the regionview was created
5665 * as part of a copy operation.
5667 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5668 layer = v->region()->layer ();
5669 initial_y = v->get_canvas_group()->position().y;
5670 initial_playlist = v->region()->playlist ();
5671 initial_position = v->region()->position ();
5672 initial_end = v->region()->position () + v->region()->length ();
5675 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5676 : Drag (e, i->canvas_item ())
5679 , _cumulative_dx (0)
5681 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5682 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5687 PatchChangeDrag::motion (GdkEvent* ev, bool)
5689 framepos_t f = adjusted_current_frame (ev);
5690 boost::shared_ptr<Region> r = _region_view->region ();
5691 f = max (f, r->position ());
5692 f = min (f, r->last_frame ());
5694 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5695 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5696 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5697 _cumulative_dx = dxu;
5701 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5703 if (!movement_occurred) {
5707 boost::shared_ptr<Region> r (_region_view->region ());
5708 framepos_t f = adjusted_current_frame (ev);
5709 f = max (f, r->position ());
5710 f = min (f, r->last_frame ());
5712 _region_view->move_patch_change (
5714 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5719 PatchChangeDrag::aborted (bool)
5721 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5725 PatchChangeDrag::setup_pointer_frame_offset ()
5727 boost::shared_ptr<Region> region = _region_view->region ();
5728 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5731 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5732 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5739 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5741 _region_view->update_drag_selection (
5743 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5747 MidiRubberbandSelectDrag::deselect_things ()
5752 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5753 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5756 _vertical_only = true;
5760 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5762 double const y = _region_view->midi_view()->y_position ();
5764 y1 = max (0.0, y1 - y);
5765 y2 = max (0.0, y2 - y);
5767 _region_view->update_vertical_drag_selection (
5770 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5775 MidiVerticalSelectDrag::deselect_things ()
5780 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5781 : RubberbandSelectDrag (e, i)
5787 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5789 if (drag_in_progress) {
5790 /* We just want to select things at the end of the drag, not during it */
5794 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5796 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5798 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5800 _editor->commit_reversible_selection_op ();
5804 EditorRubberbandSelectDrag::deselect_things ()
5806 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5808 _editor->selection->clear_tracks();
5809 _editor->selection->clear_regions();
5810 _editor->selection->clear_points ();
5811 _editor->selection->clear_lines ();
5812 _editor->selection->clear_midi_notes ();
5814 _editor->commit_reversible_selection_op();
5817 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5822 _note[0] = _note[1] = 0;
5825 NoteCreateDrag::~NoteCreateDrag ()
5831 NoteCreateDrag::grid_frames (framepos_t t) const
5834 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5836 grid_beats = Evoral::Beats(1);
5839 return _region_view->region_beats_to_region_frames (grid_beats);
5843 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5845 Drag::start_grab (event, cursor);
5847 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5849 framepos_t pf = _drags->current_pointer_frame ();
5850 framecnt_t const g = grid_frames (pf);
5852 /* Hack so that we always snap to the note that we are over, instead of snapping
5853 to the next one if we're more than halfway through the one we're over.
5855 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5859 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5860 _note[1] = _note[0];
5862 MidiStreamView* sv = _region_view->midi_stream_view ();
5863 double const x = _editor->sample_to_pixel (_note[0]);
5864 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5866 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5867 _drag_rect->set_outline_all ();
5868 _drag_rect->set_outline_color (0xffffff99);
5869 _drag_rect->set_fill_color (0xffffff66);
5873 NoteCreateDrag::motion (GdkEvent* event, bool)
5875 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5876 double const x0 = _editor->sample_to_pixel (_note[0]);
5877 double const x1 = _editor->sample_to_pixel (_note[1]);
5878 _drag_rect->set_x0 (std::min(x0, x1));
5879 _drag_rect->set_x1 (std::max(x0, x1));
5883 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5885 if (!had_movement) {
5889 framepos_t const start = min (_note[0], _note[1]);
5890 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5892 framecnt_t const g = grid_frames (start);
5893 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5895 if (_editor->snap_mode() == SnapNormal && length < g) {
5899 Evoral::Beats length_beats = max (
5900 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5902 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5906 NoteCreateDrag::y_to_region (double y) const
5909 _region_view->get_canvas_group()->canvas_to_item (x, y);
5914 NoteCreateDrag::aborted (bool)
5919 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5924 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5928 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5930 Drag::start_grab (event, cursor);
5934 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5940 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5943 distance = _drags->current_pointer_x() - grab_x();
5944 len = ar->fade_in()->back()->when;
5946 distance = grab_x() - _drags->current_pointer_x();
5947 len = ar->fade_out()->back()->when;
5950 /* how long should it be ? */
5952 new_length = len + _editor->pixel_to_sample (distance);
5954 /* now check with the region that this is legal */
5956 new_length = ar->verify_xfade_bounds (new_length, start);
5959 arv->reset_fade_in_shape_width (ar, new_length);
5961 arv->reset_fade_out_shape_width (ar, new_length);
5966 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5972 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5975 distance = _drags->current_pointer_x() - grab_x();
5976 len = ar->fade_in()->back()->when;
5978 distance = grab_x() - _drags->current_pointer_x();
5979 len = ar->fade_out()->back()->when;
5982 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5984 _editor->begin_reversible_command ("xfade trim");
5985 ar->playlist()->clear_owned_changes ();
5988 ar->set_fade_in_length (new_length);
5990 ar->set_fade_out_length (new_length);
5993 /* Adjusting the xfade may affect other regions in the playlist, so we need
5994 to get undo Commands from the whole playlist rather than just the
5998 vector<Command*> cmds;
5999 ar->playlist()->rdiff (cmds);
6000 _editor->session()->add_commands (cmds);
6001 _editor->commit_reversible_command ();
6006 CrossfadeEdgeDrag::aborted (bool)
6009 // arv->redraw_start_xfade ();
6011 // arv->redraw_end_xfade ();
6015 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6016 : Drag (e, item, true)
6017 , line (new EditorCursor (*e))
6019 line->set_position (pos);
6023 RegionCutDrag::~RegionCutDrag ()
6029 RegionCutDrag::motion (GdkEvent*, bool)
6031 framepos_t where = _drags->current_pointer_frame();
6032 _editor->snap_to (where);
6034 line->set_position (where);
6038 RegionCutDrag::finished (GdkEvent*, bool)
6040 _editor->get_track_canvas()->canvas()->re_enter();
6042 framepos_t pos = _drags->current_pointer_frame();
6046 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6052 _editor->split_regions_at (pos, rs);
6056 RegionCutDrag::aborted (bool)