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 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
104 if (!_drags.empty ()) {
105 _editor->set_follow_playhead (_old_follow_playhead, false);
109 _editor->abort_reversible_command();
115 DragManager::add (Drag* d)
117 d->set_manager (this);
118 _drags.push_back (d);
122 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
124 d->set_manager (this);
125 _drags.push_back (d);
130 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
132 /* Prevent follow playhead during the drag to be nice to the user */
133 _old_follow_playhead = _editor->follow_playhead ();
134 _editor->set_follow_playhead (false);
136 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
138 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
139 (*i)->start_grab (e, c);
143 /** Call end_grab for each active drag.
144 * @return true if any drag reported movement having occurred.
147 DragManager::end_grab (GdkEvent* e)
152 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
153 bool const t = (*i)->end_grab (e);
164 _editor->set_follow_playhead (_old_follow_playhead, false);
170 DragManager::mark_double_click ()
172 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
173 (*i)->set_double_click (true);
178 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
182 /* calling this implies that we expect the event to have canvas
185 * Can we guarantee that this is true?
188 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
190 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
191 bool const t = (*i)->motion_handler (e, from_autoscroll);
192 /* run all handlers; return true if at least one of them
193 returns true (indicating that the event has been handled).
205 DragManager::have_item (ArdourCanvas::Item* i) const
207 list<Drag*>::const_iterator j = _drags.begin ();
208 while (j != _drags.end() && (*j)->item () != i) {
212 return j != _drags.end ();
215 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
218 , _pointer_frame_offset (0)
219 , _x_constrained (false)
220 , _y_constrained (false)
221 , _trackview_only (trackview_only)
222 , _move_threshold_passed (false)
223 , _starting_point_passed (false)
224 , _initially_vertical (false)
225 , _was_double_click (false)
226 , _raw_grab_frame (0)
228 , _last_pointer_frame (0)
235 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
241 _cursor_ctx = CursorContext::create (*_editor, cursor);
243 _cursor_ctx->change (cursor);
250 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
253 /* we set up x/y dragging constraints on first move */
255 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
257 setup_pointer_frame_offset ();
258 _grab_frame = adjusted_frame (_raw_grab_frame, event);
259 _last_pointer_frame = _grab_frame;
260 _last_pointer_x = _grab_x;
262 if (_trackview_only) {
263 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
266 _last_pointer_y = _grab_y;
270 if (!_editor->cursors()->is_invalid (cursor)) {
271 /* CAIROCANVAS need a variant here that passes *cursor */
272 _cursor_ctx = CursorContext::create (*_editor, cursor);
275 if (_editor->session() && _editor->session()->transport_rolling()) {
278 _was_rolling = false;
281 switch (_editor->snap_type()) {
282 case SnapToRegionStart:
283 case SnapToRegionEnd:
284 case SnapToRegionSync:
285 case SnapToRegionBoundary:
286 _editor->build_region_boundary_cache ();
293 /** Call to end a drag `successfully'. Ungrabs item and calls
294 * subclass' finished() method.
296 * @param event GDK event, or 0.
297 * @return true if some movement occurred, otherwise false.
300 Drag::end_grab (GdkEvent* event)
302 _editor->stop_canvas_autoscroll ();
306 finished (event, _move_threshold_passed);
308 _editor->verbose_cursor()->hide ();
311 return _move_threshold_passed;
315 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
319 if (f > _pointer_frame_offset) {
320 pos = f - _pointer_frame_offset;
324 _editor->snap_to_with_modifier (pos, event);
331 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
333 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
337 Drag::snap_delta (guint state) const
339 if (ArdourKeyboard::indicates_snap_delta (state)) {
347 Drag::current_pointer_x() const
349 return _drags->current_pointer_x ();
353 Drag::current_pointer_y () const
355 if (!_trackview_only) {
356 return _drags->current_pointer_y ();
359 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
363 Drag::setup_snap_delta (framepos_t pos)
365 framepos_t temp = pos;
366 _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
367 _snap_delta = temp - pos;
371 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
373 /* check to see if we have moved in any way that matters since the last motion event */
374 if (_move_threshold_passed &&
375 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
376 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
380 pair<framecnt_t, int> const threshold = move_threshold ();
382 bool const old_move_threshold_passed = _move_threshold_passed;
384 if (!_move_threshold_passed) {
386 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
387 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
389 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
392 if (active (_editor->mouse_mode) && _move_threshold_passed) {
394 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
396 if (old_move_threshold_passed != _move_threshold_passed) {
400 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
401 _initially_vertical = true;
403 _initially_vertical = false;
405 /** check constraints for this drag.
406 * Note that the current convention is to use "contains" for
407 * key modifiers during motion and "equals" when initiating a drag.
408 * In this case we haven't moved yet, so "equals" applies here.
410 if (Config->get_edit_mode() != Lock) {
411 if (event->motion.state & Gdk::BUTTON2_MASK) {
412 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
413 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
414 _x_constrained = false;
415 _y_constrained = true;
417 _x_constrained = true;
418 _y_constrained = false;
420 } else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
421 // if dragging normally, the motion is constrained to the first direction of movement.
422 if (_initially_vertical) {
423 _x_constrained = true;
424 _y_constrained = false;
426 _x_constrained = false;
427 _y_constrained = true;
431 if (event->button.state & Gdk::BUTTON2_MASK) {
432 _x_constrained = false;
434 _x_constrained = true;
436 _y_constrained = false;
440 if (!from_autoscroll) {
441 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
444 if (!_editor->autoscroll_active() || from_autoscroll) {
447 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
449 motion (event, first_move && !_starting_point_passed);
451 if (first_move && !_starting_point_passed) {
452 _starting_point_passed = true;
455 _last_pointer_x = _drags->current_pointer_x ();
456 _last_pointer_y = current_pointer_y ();
457 _last_pointer_frame = adjusted_current_frame (event);
467 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
475 aborted (_move_threshold_passed);
477 _editor->stop_canvas_autoscroll ();
478 _editor->verbose_cursor()->hide ();
482 Drag::show_verbose_cursor_time (framepos_t frame)
484 _editor->verbose_cursor()->set_time (frame);
485 _editor->verbose_cursor()->show ();
489 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
491 _editor->verbose_cursor()->set_duration (start, end);
492 _editor->verbose_cursor()->show ();
496 Drag::show_verbose_cursor_text (string const & text)
498 _editor->verbose_cursor()->set (text);
499 _editor->verbose_cursor()->show ();
502 boost::shared_ptr<Region>
503 Drag::add_midi_region (MidiTimeAxisView* view)
505 if (_editor->session()) {
506 const TempoMap& map (_editor->session()->tempo_map());
507 framecnt_t pos = grab_frame();
508 const Meter& m = map.meter_at (pos);
509 /* not that the frame rate used here can be affected by pull up/down which
512 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
513 return view->add_region (grab_frame(), len, true);
516 return boost::shared_ptr<Region>();
519 struct EditorOrderTimeAxisViewSorter {
520 bool operator() (TimeAxisView* a, TimeAxisView* b) {
521 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
522 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
524 return ra->route()->order_key () < rb->route()->order_key ();
528 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
533 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
535 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
536 as some of the regions we are dragging may be on such tracks.
539 TrackViewList track_views = _editor->track_views;
540 track_views.sort (EditorOrderTimeAxisViewSorter ());
542 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
543 _time_axis_views.push_back (*i);
545 TimeAxisView::Children children_list = (*i)->get_child_list ();
546 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
547 _time_axis_views.push_back (j->get());
551 /* the list of views can be empty at this point if this is a region list-insert drag
554 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
555 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
558 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
562 RegionDrag::region_going_away (RegionView* v)
564 list<DraggingView>::iterator i = _views.begin ();
565 while (i != _views.end() && i->view != v) {
569 if (i != _views.end()) {
574 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
575 * or -1 if it is not found.
578 RegionDrag::find_time_axis_view (TimeAxisView* t) const
581 int const N = _time_axis_views.size ();
582 while (i < N && _time_axis_views[i] != t) {
586 if (_time_axis_views[i] != t) {
593 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
594 : RegionDrag (e, i, p, v)
596 , _ignore_video_lock (false)
598 , _last_pointer_time_axis_view (0)
599 , _last_pointer_layer (0)
604 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
608 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
610 Drag::start_grab (event, cursor);
611 setup_snap_delta (_last_frame_position);
613 show_verbose_cursor_time (_last_frame_position);
615 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
617 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
618 assert(_last_pointer_time_axis_view >= 0);
619 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
622 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
623 _ignore_video_lock = true;
627 /* cross track dragging seems broken here. disabled for now. */
628 _y_constrained = true;
633 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
635 /* compute the amount of pointer motion in frames, and where
636 the region would be if we moved it by that much.
638 *pending_region_position = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
640 framepos_t sync_frame;
641 framecnt_t sync_offset;
644 sync_offset = _primary->region()->sync_offset (sync_dir);
646 /* we don't handle a sync point that lies before zero.
648 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
650 sync_frame = *pending_region_position + (sync_dir * sync_offset);
652 _editor->snap_to_with_modifier (sync_frame, event);
654 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - snap_delta (event->button.state);
657 *pending_region_position = _last_frame_position;
660 if (*pending_region_position > max_framepos - _primary->region()->length()) {
661 *pending_region_position = _last_frame_position;
666 bool const x_move_allowed = !_x_constrained;
668 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
670 /* x movement since last time (in pixels) */
671 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
673 /* total x movement */
674 framecnt_t total_dx = *pending_region_position;
675 if (regions_came_from_canvas()) {
676 total_dx = total_dx - grab_frame ();
679 /* check that no regions have gone off the start of the session */
680 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
681 if ((i->view->region()->position() + total_dx) < 0) {
683 *pending_region_position = _last_frame_position;
694 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
700 const int tavsize = _time_axis_views.size();
701 const int dt = delta > 0 ? +1 : -1;
703 int target = start + delta - skip;
705 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
706 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
708 while (current >= 0 && current != target) {
710 if (current < 0 && dt < 0) {
713 if (current >= tavsize && dt > 0) {
716 if (current < 0 || current >= tavsize) {
720 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
721 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
725 if (distance_only && current == start + delta) {
733 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
735 if (_y_constrained) {
739 const int tavsize = _time_axis_views.size();
740 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
741 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
742 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
744 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
745 /* already in the drop zone */
746 if (delta_track >= 0) {
747 /* downward motion - OK if others are still not in the dropzone */
756 } else if (n >= tavsize) {
757 /* downward motion into drop zone. That's fine. */
761 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
762 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
763 /* not a track, or the wrong type */
767 double const l = i->layer + delta_layer;
769 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
770 mode to allow the user to place a region below another on layer 0.
772 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
773 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
774 If it has, the layers will be munged later anyway, so it's ok.
780 /* all regions being dragged are ok with this change */
784 struct DraggingViewSorter {
785 bool operator() (const DraggingView& a, const DraggingView& b) {
786 return a.time_axis_view < b.time_axis_view;
791 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
793 double delta_layer = 0;
794 int delta_time_axis_view = 0;
795 int current_pointer_time_axis_view = -1;
797 assert (!_views.empty ());
799 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
801 /* Find the TimeAxisView that the pointer is now over */
802 const double cur_y = current_pointer_y ();
803 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
804 TimeAxisView* tv = r.first;
806 if (!tv && cur_y < 0) {
807 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
811 /* find drop-zone y-position */
812 Coord last_track_bottom_edge;
813 last_track_bottom_edge = 0;
814 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
815 if (!(*t)->hidden()) {
816 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
821 if (tv && tv->view()) {
822 /* the mouse is over a track */
823 double layer = r.second;
825 if (first_move && tv->view()->layer_display() == Stacked) {
826 tv->view()->set_layer_display (Expanded);
829 /* Here's the current pointer position in terms of time axis view and layer */
830 current_pointer_time_axis_view = find_time_axis_view (tv);
831 assert(current_pointer_time_axis_view >= 0);
833 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
835 /* Work out the change in y */
837 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
838 if (!rtv || !rtv->is_track()) {
839 /* ignore busses early on. we can't move any regions on them */
840 } else if (_last_pointer_time_axis_view < 0) {
841 /* Was in the drop-zone, now over a track.
842 * Hence it must be an upward move (from the bottom)
844 * track_index is still -1, so delta must be set to
845 * move up the correct number of tracks from the bottom.
847 * This is necessary because steps may be skipped if
848 * the bottom-most track is not a valid target and/or
849 * if there are hidden tracks at the bottom.
850 * Hence the initial offset (_ddropzone) as well as the
851 * last valid pointer position (_pdropzone) need to be
852 * taken into account.
854 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
856 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
859 /* TODO needs adjustment per DraggingView,
861 * e.g. select one region on the top-layer of a track
862 * and one region which is at the bottom-layer of another track
865 * Indicated drop-zones and layering is wrong.
866 * and may infer additional layers on the target-track
867 * (depending how many layers the original track had).
869 * Or select two regions (different layers) on a same track,
870 * move across a non-layer track.. -> layering info is lost.
871 * on drop either of the regions may be on top.
873 * Proposed solution: screw it :) well,
874 * don't use delta_layer, use an absolute value
875 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
876 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
877 * 3) iterate over all DraggingView, find the one that is over the track with most layers
878 * 4) proportionally scale layer to layers available on target
880 delta_layer = current_pointer_layer - _last_pointer_layer;
883 /* for automation lanes, there is a TimeAxisView but no ->view()
884 * if (!tv) -> dropzone
886 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
887 /* Moving into the drop-zone.. */
888 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
889 /* delta_time_axis_view may not be sufficient to move into the DZ
890 * the mouse may enter it, but it may not be a valid move due to
893 * -> remember the delta needed to move into the dropzone
895 _ddropzone = delta_time_axis_view;
896 /* ..but subtract hidden tracks (or routes) at the bottom.
897 * we silently move mover them
899 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
900 - _time_axis_views.size();
902 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
903 /* move around inside the zone.
904 * This allows to move further down until all regions are in the zone.
906 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
907 assert(ptr_y >= last_track_bottom_edge);
908 assert(_ddropzone > 0);
910 /* calculate mouse position in 'tracks' below last track. */
911 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
912 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
914 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
916 delta_time_axis_view = dzpos - _pdropzone;
917 } else if (dzpos < _pdropzone && _ndropzone > 0) {
918 // move up inside the DZ
919 delta_time_axis_view = dzpos - _pdropzone;
923 /* Work out the change in x */
924 framepos_t pending_region_position;
925 double const x_delta = compute_x_delta (event, &pending_region_position);
926 _last_frame_position = pending_region_position;
928 /* calculate hidden tracks in current y-axis delta */
930 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
931 /* The mouse is more than one track below the dropzone.
932 * distance calculation is not needed (and would not work, either
933 * because the dropzone is "packed").
935 * Except when [partially] moving regions out of dropzone in a large step.
936 * (the mouse may or may not remain in the DZ)
937 * Hidden tracks at the bottom of the TAV need to be skipped.
939 * This also handles the case if the mouse entered the DZ
940 * in a large step (exessive delta), either due to fast-movement,
941 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
943 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
944 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
946 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
947 -_time_axis_views.size() - dt;
950 else if (_last_pointer_time_axis_view < 0) {
951 /* Moving out of the zone. Check for hidden tracks at the bottom. */
952 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
953 -_time_axis_views.size() - delta_time_axis_view;
955 /* calculate hidden tracks that are skipped by the pointer movement */
956 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
957 - _last_pointer_time_axis_view
958 - delta_time_axis_view;
961 /* Verify change in y */
962 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
963 /* this y movement is not allowed, so do no y movement this time */
964 delta_time_axis_view = 0;
969 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
970 /* haven't reached next snap point, and we're not switching
971 trackviews nor layers. nothing to do.
976 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
977 PlaylistDropzoneMap playlist_dropzone_map;
978 _ndropzone = 0; // number of elements currently in the dropzone
981 /* sort views by time_axis.
982 * This retains track order in the dropzone, regardless
983 * of actual selection order
985 _views.sort (DraggingViewSorter());
987 /* count number of distinct tracks of all regions
988 * being dragged, used for dropzone.
991 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
992 if (i->time_axis_view != prev_track) {
993 prev_track = i->time_axis_view;
999 _views.back().time_axis_view -
1000 _views.front().time_axis_view;
1002 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1003 - _views.back().time_axis_view;
1005 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1009 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1011 RegionView* rv = i->view;
1016 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1023 /* reparent the regionview into a group above all
1027 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1028 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1029 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1030 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1031 /* move the item so that it continues to appear at the
1032 same location now that its parent has changed.
1034 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1037 /* If we have moved tracks, we'll fudge the layer delta so that the
1038 region gets moved back onto layer 0 on its new track; this avoids
1039 confusion when dragging regions from non-zero layers onto different
1042 double this_delta_layer = delta_layer;
1043 if (delta_time_axis_view != 0) {
1044 this_delta_layer = - i->layer;
1047 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1049 int track_index = i->time_axis_view + this_delta_time_axis_view;
1050 assert(track_index >= 0);
1052 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1053 /* Track is in the Dropzone */
1055 i->time_axis_view = track_index;
1056 assert(i->time_axis_view >= (int) _time_axis_views.size());
1059 double yposition = 0;
1060 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1061 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1064 /* store index of each new playlist as a negative count, starting at -1 */
1066 if (pdz == playlist_dropzone_map.end()) {
1067 /* compute where this new track (which doesn't exist yet) will live
1070 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1072 /* How high is this region view ? */
1074 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1075 ArdourCanvas::Rect bbox;
1078 bbox = obbox.get ();
1081 last_track_bottom_edge += bbox.height();
1083 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1086 yposition = pdz->second;
1089 /* values are zero or negative, hence the use of min() */
1090 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1095 /* The TimeAxisView that this region is now over */
1096 TimeAxisView* current_tv = _time_axis_views[track_index];
1098 /* Ensure it is moved from stacked -> expanded if appropriate */
1099 if (current_tv->view()->layer_display() == Stacked) {
1100 current_tv->view()->set_layer_display (Expanded);
1103 /* We're only allowed to go -ve in layer on Expanded views */
1104 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1105 this_delta_layer = - i->layer;
1109 rv->set_height (current_tv->view()->child_height ());
1111 /* Update show/hidden status as the region view may have come from a hidden track,
1112 or have moved to one.
1114 if (current_tv->hidden ()) {
1115 rv->get_canvas_group()->hide ();
1117 rv->get_canvas_group()->show ();
1120 /* Update the DraggingView */
1121 i->time_axis_view = track_index;
1122 i->layer += this_delta_layer;
1125 _editor->mouse_brush_insert_region (rv, pending_region_position);
1129 /* Get the y coordinate of the top of the track that this region is now over */
1130 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1132 /* And adjust for the layer that it should be on */
1133 StreamView* cv = current_tv->view ();
1134 switch (cv->layer_display ()) {
1138 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1141 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1145 /* need to get the parent of the regionview
1146 * canvas group and get its position in
1147 * equivalent coordinate space as the trackview
1148 * we are now dragging over.
1151 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1156 /* Now move the region view */
1157 rv->move (x_delta, y_delta);
1159 } /* foreach region */
1161 _total_x_delta += x_delta;
1163 if (x_delta != 0 && !_brushing) {
1164 show_verbose_cursor_time (_last_frame_position);
1167 /* keep track of pointer movement */
1169 /* the pointer is currently over a time axis view */
1171 if (_last_pointer_time_axis_view < 0) {
1172 /* last motion event was not over a time axis view
1173 * or last y-movement out of the dropzone was not valid
1176 if (delta_time_axis_view < 0) {
1177 /* in the drop zone, moving up */
1179 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1180 * We do not use negative _last_pointer_time_axis_view because
1181 * the dropzone is "packed" (the actual track offset is ignored)
1183 * As opposed to the actual number
1184 * of elements in the dropzone (_ndropzone)
1185 * _pdropzone is not constrained. This is necessary
1186 * to allow moving multiple regions with y-distance
1189 * There can be 0 elements in the dropzone,
1190 * even though the drag-pointer is inside the DZ.
1193 * [ Audio-track, Midi-track, Audio-track, DZ ]
1194 * move regions from both audio tracks at the same time into the
1195 * DZ by grabbing the region in the bottom track.
1197 assert(current_pointer_time_axis_view >= 0);
1198 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1202 /* only move out of the zone if the movement is OK */
1203 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1204 assert(delta_time_axis_view < 0);
1205 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1206 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1207 * the current position can be calculated as follows:
1209 // a well placed oofus attack can still throw this off.
1210 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1211 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1214 /* last motion event was also over a time axis view */
1215 _last_pointer_time_axis_view += delta_time_axis_view;
1216 assert(_last_pointer_time_axis_view >= 0);
1221 /* the pointer is not over a time axis view */
1222 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1223 _pdropzone += delta_time_axis_view - delta_skip;
1224 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1227 _last_pointer_layer += delta_layer;
1231 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1233 if (_copy && first_move) {
1234 if (_x_constrained && !_brushing) {
1235 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1236 } else if (!_brushing) {
1237 _editor->begin_reversible_command (Operations::region_copy);
1238 } else if (_brushing) {
1239 _editor->begin_reversible_command (Operations::drag_region_brush);
1241 /* duplicate the regionview(s) and region(s) */
1243 list<DraggingView> new_regionviews;
1245 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1247 RegionView* rv = i->view;
1248 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1249 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1251 const boost::shared_ptr<const Region> original = rv->region();
1252 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1253 region_copy->set_position (original->position());
1254 /* need to set this so that the drop zone code can work. This doesn't
1255 actually put the region into the playlist, but just sets a weak pointer
1258 region_copy->set_playlist (original->playlist());
1262 boost::shared_ptr<AudioRegion> audioregion_copy
1263 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1265 nrv = new AudioRegionView (*arv, audioregion_copy);
1267 boost::shared_ptr<MidiRegion> midiregion_copy
1268 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1269 nrv = new MidiRegionView (*mrv, midiregion_copy);
1274 nrv->get_canvas_group()->show ();
1275 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1277 /* swap _primary to the copy */
1279 if (rv == _primary) {
1283 /* ..and deselect the one we copied */
1285 rv->set_selected (false);
1288 if (!new_regionviews.empty()) {
1290 /* reflect the fact that we are dragging the copies */
1292 _views = new_regionviews;
1294 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1297 } else if (!_copy && first_move) {
1298 if (_x_constrained && !_brushing) {
1299 _editor->begin_reversible_command (_("fixed time region drag"));
1300 } else if (!_brushing) {
1301 _editor->begin_reversible_command (Operations::region_drag);
1302 } else if (_brushing) {
1303 _editor->begin_reversible_command (Operations::drag_region_brush);
1306 RegionMotionDrag::motion (event, first_move);
1310 RegionMotionDrag::finished (GdkEvent *, bool)
1312 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1313 if (!(*i)->view()) {
1317 if ((*i)->view()->layer_display() == Expanded) {
1318 (*i)->view()->set_layer_display (Stacked);
1324 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1326 RegionMotionDrag::finished (ev, movement_occurred);
1328 if (!movement_occurred) {
1332 if (was_double_click() && !_views.empty()) {
1333 DraggingView dv = _views.front();
1334 dv.view->show_region_editor ();
1341 assert (!_views.empty ());
1343 /* We might have hidden region views so that they weren't visible during the drag
1344 (when they have been reparented). Now everything can be shown again, as region
1345 views are back in their track parent groups.
1347 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1348 i->view->get_canvas_group()->show ();
1351 bool const changed_position = (_last_frame_position != _primary->region()->position());
1352 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1353 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1373 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1377 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1379 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1384 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1385 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1386 uint32_t output_chan = region->n_channels();
1387 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1388 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1390 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name());
1391 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1393 rtav->set_height (original->current_height());
1397 ChanCount one_midi_port (DataType::MIDI, 1);
1398 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1399 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1400 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1402 rtav->set_height (original->current_height());
1407 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1413 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1415 RegionSelection new_views;
1416 PlaylistSet modified_playlists;
1417 RouteTimeAxisView* new_time_axis_view = 0;
1420 /* all changes were made during motion event handlers */
1422 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1426 _editor->commit_reversible_command ();
1430 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1431 PlaylistMapping playlist_mapping;
1433 /* insert the regions into their new playlists */
1434 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1436 RouteTimeAxisView* dest_rtv = 0;
1438 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1444 if (changed_position && !_x_constrained) {
1445 where = i->view->region()->position() - drag_delta;
1447 where = i->view->region()->position();
1450 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1451 /* dragged to drop zone */
1453 PlaylistMapping::iterator pm;
1455 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1456 /* first region from this original playlist: create a new track */
1457 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1458 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1459 dest_rtv = new_time_axis_view;
1461 /* we already created a new track for regions from this playlist, use it */
1462 dest_rtv = pm->second;
1465 /* destination time axis view is the one we dragged to */
1466 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1469 if (dest_rtv != 0) {
1470 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1471 if (new_view != 0) {
1472 new_views.push_back (new_view);
1476 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1477 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1480 list<DraggingView>::const_iterator next = i;
1486 /* If we've created new regions either by copying or moving
1487 to a new track, we want to replace the old selection with the new ones
1490 if (new_views.size() > 0) {
1491 _editor->selection->set (new_views);
1494 /* write commands for the accumulated diffs for all our modified playlists */
1495 add_stateful_diff_commands_for_playlists (modified_playlists);
1497 _editor->commit_reversible_command ();
1501 RegionMoveDrag::finished_no_copy (
1502 bool const changed_position,
1503 bool const changed_tracks,
1504 framecnt_t const drag_delta
1507 RegionSelection new_views;
1508 PlaylistSet modified_playlists;
1509 PlaylistSet frozen_playlists;
1510 set<RouteTimeAxisView*> views_to_update;
1511 RouteTimeAxisView* new_time_axis_view = 0;
1513 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1514 PlaylistMapping playlist_mapping;
1516 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1518 RegionView* rv = i->view;
1519 RouteTimeAxisView* dest_rtv = 0;
1521 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1526 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1527 /* dragged to drop zone */
1529 PlaylistMapping::iterator pm;
1531 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1532 /* first region from this original playlist: create a new track */
1533 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1534 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1535 dest_rtv = new_time_axis_view;
1537 /* we already created a new track for regions from this playlist, use it */
1538 dest_rtv = pm->second;
1542 /* destination time axis view is the one we dragged to */
1543 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1548 double const dest_layer = i->layer;
1550 views_to_update.insert (dest_rtv);
1554 if (changed_position && !_x_constrained) {
1555 where = rv->region()->position() - drag_delta;
1557 where = rv->region()->position();
1560 if (changed_tracks) {
1562 /* insert into new playlist */
1564 RegionView* new_view = insert_region_into_playlist (
1565 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1568 if (new_view == 0) {
1573 new_views.push_back (new_view);
1575 /* remove from old playlist */
1577 /* the region that used to be in the old playlist is not
1578 moved to the new one - we use a copy of it. as a result,
1579 any existing editor for the region should no longer be
1582 rv->hide_region_editor();
1585 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1589 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1591 /* this movement may result in a crossfade being modified, or a layering change,
1592 so we need to get undo data from the playlist as well as the region.
1595 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1597 playlist->clear_changes ();
1600 rv->region()->clear_changes ();
1603 motion on the same track. plonk the previously reparented region
1604 back to its original canvas group (its streamview).
1605 No need to do anything for copies as they are fake regions which will be deleted.
1608 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1609 rv->get_canvas_group()->set_y_position (i->initial_y);
1612 /* just change the model */
1613 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1614 playlist->set_layer (rv->region(), dest_layer);
1617 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1619 r = frozen_playlists.insert (playlist);
1622 playlist->freeze ();
1625 rv->region()->set_position (where);
1626 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1629 if (changed_tracks) {
1631 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1632 was selected in all of them, then removing it from a playlist will have removed all
1633 trace of it from _views (i.e. there were N regions selected, we removed 1,
1634 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1635 corresponding regionview, and _views is now empty).
1637 This could have invalidated any and all iterators into _views.
1639 The heuristic we use here is: if the region selection is empty, break out of the loop
1640 here. if the region selection is not empty, then restart the loop because we know that
1641 we must have removed at least the region(view) we've just been working on as well as any
1642 that we processed on previous iterations.
1644 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1645 we can just iterate.
1649 if (_views.empty()) {
1660 /* If we've created new regions either by copying or moving
1661 to a new track, we want to replace the old selection with the new ones
1664 if (new_views.size() > 0) {
1665 _editor->selection->set (new_views);
1668 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1672 /* write commands for the accumulated diffs for all our modified playlists */
1673 add_stateful_diff_commands_for_playlists (modified_playlists);
1674 /* applies to _brushing */
1675 _editor->commit_reversible_command ();
1677 /* We have futzed with the layering of canvas items on our streamviews.
1678 If any region changed layer, this will have resulted in the stream
1679 views being asked to set up their region views, and all will be well.
1680 If not, we might now have badly-ordered region views. Ask the StreamViews
1681 involved to sort themselves out, just in case.
1684 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1685 (*i)->view()->playlist_layered ((*i)->track ());
1689 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1690 * @param region Region to remove.
1691 * @param playlist playlist To remove from.
1692 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1693 * that clear_changes () is only called once per playlist.
1696 RegionMoveDrag::remove_region_from_playlist (
1697 boost::shared_ptr<Region> region,
1698 boost::shared_ptr<Playlist> playlist,
1699 PlaylistSet& modified_playlists
1702 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1705 playlist->clear_changes ();
1708 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1712 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1713 * clearing the playlist's diff history first if necessary.
1714 * @param region Region to insert.
1715 * @param dest_rtv Destination RouteTimeAxisView.
1716 * @param dest_layer Destination layer.
1717 * @param where Destination position.
1718 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1719 * that clear_changes () is only called once per playlist.
1720 * @return New RegionView, or 0 if no insert was performed.
1723 RegionMoveDrag::insert_region_into_playlist (
1724 boost::shared_ptr<Region> region,
1725 RouteTimeAxisView* dest_rtv,
1728 PlaylistSet& modified_playlists
1731 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1732 if (!dest_playlist) {
1736 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1737 _new_region_view = 0;
1738 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1740 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1741 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1743 dest_playlist->clear_changes ();
1746 dest_playlist->add_region (region, where);
1748 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1749 dest_playlist->set_layer (region, dest_layer);
1754 assert (_new_region_view);
1756 return _new_region_view;
1760 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1762 _new_region_view = rv;
1766 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1768 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1769 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1771 _editor->session()->add_command (c);
1780 RegionMoveDrag::aborted (bool movement_occurred)
1784 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1785 list<DraggingView>::const_iterator next = i;
1794 RegionMotionDrag::aborted (movement_occurred);
1799 RegionMotionDrag::aborted (bool)
1801 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1803 StreamView* sview = (*i)->view();
1806 if (sview->layer_display() == Expanded) {
1807 sview->set_layer_display (Stacked);
1812 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1813 RegionView* rv = i->view;
1814 TimeAxisView* tv = &(rv->get_time_axis_view ());
1815 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1817 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1818 rv->get_canvas_group()->set_y_position (0);
1820 rv->move (-_total_x_delta, 0);
1821 rv->set_height (rtv->view()->child_height ());
1825 /** @param b true to brush, otherwise false.
1826 * @param c true to make copies of the regions being moved, otherwise false.
1828 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1829 : RegionMotionDrag (e, i, p, v, b)
1832 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1835 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1836 if (rtv && rtv->is_track()) {
1837 speed = rtv->track()->speed ();
1840 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1844 RegionMoveDrag::setup_pointer_frame_offset ()
1846 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1849 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1850 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1852 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1854 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1855 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1857 _primary = v->view()->create_region_view (r, false, false);
1859 _primary->get_canvas_group()->show ();
1860 _primary->set_position (pos, 0);
1861 _views.push_back (DraggingView (_primary, this, v));
1863 _last_frame_position = pos;
1865 _item = _primary->get_canvas_group ();
1869 RegionInsertDrag::finished (GdkEvent *, bool)
1871 int pos = _views.front().time_axis_view;
1872 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1874 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1876 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1877 _primary->get_canvas_group()->set_y_position (0);
1879 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1881 _editor->begin_reversible_command (Operations::insert_region);
1882 playlist->clear_changes ();
1883 playlist->add_region (_primary->region (), _last_frame_position);
1885 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1886 if (Config->get_edit_mode() == Ripple) {
1887 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1890 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1891 _editor->commit_reversible_command ();
1899 RegionInsertDrag::aborted (bool)
1906 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1907 : RegionMoveDrag (e, i, p, v, false, false)
1909 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1912 struct RegionSelectionByPosition {
1913 bool operator() (RegionView*a, RegionView* b) {
1914 return a->region()->position () < b->region()->position();
1919 RegionSpliceDrag::motion (GdkEvent* event, bool)
1921 /* Which trackview is this ? */
1923 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1924 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1926 /* The region motion is only processed if the pointer is over
1930 if (!tv || !tv->is_track()) {
1931 /* To make sure we hide the verbose canvas cursor when the mouse is
1932 not held over an audio track.
1934 _editor->verbose_cursor()->hide ();
1937 _editor->verbose_cursor()->show ();
1942 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1948 RegionSelection copy;
1949 _editor->selection->regions.by_position(copy);
1951 framepos_t const pf = adjusted_current_frame (event);
1953 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1955 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1961 boost::shared_ptr<Playlist> playlist;
1963 if ((playlist = atv->playlist()) == 0) {
1967 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1972 if (pf < (*i)->region()->last_frame() + 1) {
1976 if (pf > (*i)->region()->first_frame()) {
1982 playlist->shuffle ((*i)->region(), dir);
1987 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1989 RegionMoveDrag::finished (event, movement_occurred);
1993 RegionSpliceDrag::aborted (bool)
2003 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2006 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2008 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2009 RegionSelection to_ripple;
2010 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2011 if ((*i)->position() >= where) {
2012 to_ripple.push_back (rtv->view()->find_view(*i));
2016 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2017 if (!exclude.contains (*i)) {
2018 // the selection has already been added to _views
2020 if (drag_in_progress) {
2021 // do the same things that RegionMotionDrag::motion does when
2022 // first_move is true, for the region views that we're adding
2023 // to _views this time
2026 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2027 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2028 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2029 rvg->reparent (_editor->_drag_motion_group);
2031 // we only need to move in the y direction
2032 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2037 _views.push_back (DraggingView (*i, this, tav));
2043 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2046 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2047 // we added all the regions after the selection
2049 std::list<DraggingView>::iterator to_erase = i++;
2050 if (!_editor->selection->regions.contains (to_erase->view)) {
2051 // restore the non-selected regions to their original playlist & positions,
2052 // and then ripple them back by the length of the regions that were dragged away
2053 // do the same things as RegionMotionDrag::aborted
2055 RegionView *rv = to_erase->view;
2056 TimeAxisView* tv = &(rv->get_time_axis_view ());
2057 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2060 // plonk them back onto their own track
2061 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2062 rv->get_canvas_group()->set_y_position (0);
2066 // move the underlying region to match the view
2067 rv->region()->set_position (rv->region()->position() + amount);
2069 // restore the view to match the underlying region's original position
2070 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2073 rv->set_height (rtv->view()->child_height ());
2074 _views.erase (to_erase);
2080 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2082 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2084 return allow_moves_across_tracks;
2092 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2093 : RegionMoveDrag (e, i, p, v, false, false)
2095 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2096 // compute length of selection
2097 RegionSelection selected_regions = _editor->selection->regions;
2098 selection_length = selected_regions.end_frame() - selected_regions.start();
2100 // we'll only allow dragging to another track in ripple mode if all the regions
2101 // being dragged start off on the same track
2102 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2105 exclude = new RegionList;
2106 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2107 exclude->push_back((*i)->region());
2110 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2111 RegionSelection copy;
2112 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2114 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2115 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2117 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2118 // find ripple start point on each applicable playlist
2119 RegionView *first_selected_on_this_track = NULL;
2120 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2121 if ((*i)->region()->playlist() == (*pi)) {
2122 // region is on this playlist - it's the first, because they're sorted
2123 first_selected_on_this_track = *i;
2127 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2128 add_all_after_to_views (
2129 &first_selected_on_this_track->get_time_axis_view(),
2130 first_selected_on_this_track->region()->position(),
2131 selected_regions, false);
2134 if (allow_moves_across_tracks) {
2135 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2143 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2145 /* Which trackview is this ? */
2147 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2148 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2150 /* The region motion is only processed if the pointer is over
2154 if (!tv || !tv->is_track()) {
2155 /* To make sure we hide the verbose canvas cursor when the mouse is
2156 not held over an audiotrack.
2158 _editor->verbose_cursor()->hide ();
2162 framepos_t where = adjusted_current_frame (event);
2163 assert (where >= 0);
2165 double delta = compute_x_delta (event, &after);
2167 framecnt_t amount = _editor->pixel_to_sample (delta);
2169 if (allow_moves_across_tracks) {
2170 // all the originally selected regions were on the same track
2172 framecnt_t adjust = 0;
2173 if (prev_tav && tv != prev_tav) {
2174 // dragged onto a different track
2175 // remove the unselected regions from _views, restore them to their original positions
2176 // and add the regions after the drop point on the new playlist to _views instead.
2177 // undo the effect of rippling the previous playlist, and include the effect of removing
2178 // the dragged region(s) from this track
2180 remove_unselected_from_views (prev_amount, false);
2181 // ripple previous playlist according to the regions that have been removed onto the new playlist
2182 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2185 // move just the selected regions
2186 RegionMoveDrag::motion(event, first_move);
2188 // ensure that the ripple operation on the new playlist inserts selection_length time
2189 adjust = selection_length;
2190 // ripple the new current playlist
2191 tv->playlist()->ripple (where, amount+adjust, exclude);
2193 // add regions after point where drag entered this track to subsequent ripples
2194 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2197 // motion on same track
2198 RegionMoveDrag::motion(event, first_move);
2202 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2203 prev_position = where;
2205 // selection encompasses multiple tracks - just drag
2206 // cross-track drags are forbidden
2207 RegionMoveDrag::motion(event, first_move);
2210 if (!_x_constrained) {
2211 prev_amount += amount;
2214 _last_frame_position = after;
2218 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2220 if (!movement_occurred) {
2224 if (was_double_click() && !_views.empty()) {
2225 DraggingView dv = _views.front();
2226 dv.view->show_region_editor ();
2233 _editor->begin_reversible_command(_("Ripple drag"));
2235 // remove the regions being rippled from the dragging view, updating them to
2236 // their new positions
2237 remove_unselected_from_views (prev_amount, true);
2239 if (allow_moves_across_tracks) {
2241 // if regions were dragged across tracks, we've rippled any later
2242 // regions on the track the regions were dragged off, so we need
2243 // to add the original track to the undo record
2244 orig_tav->playlist()->clear_changes();
2245 vector<Command*> cmds;
2246 orig_tav->playlist()->rdiff (cmds);
2247 _editor->session()->add_commands (cmds);
2249 if (prev_tav && prev_tav != orig_tav) {
2250 prev_tav->playlist()->clear_changes();
2251 vector<Command*> cmds;
2252 prev_tav->playlist()->rdiff (cmds);
2253 _editor->session()->add_commands (cmds);
2256 // selection spanned multiple tracks - all will need adding to undo record
2258 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2259 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2261 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2262 (*pi)->clear_changes();
2263 vector<Command*> cmds;
2264 (*pi)->rdiff (cmds);
2265 _editor->session()->add_commands (cmds);
2269 // other modified playlists are added to undo by RegionMoveDrag::finished()
2270 RegionMoveDrag::finished (event, movement_occurred);
2271 _editor->commit_reversible_command();
2275 RegionRippleDrag::aborted (bool movement_occurred)
2277 RegionMoveDrag::aborted (movement_occurred);
2282 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2284 _view (dynamic_cast<MidiTimeAxisView*> (v))
2286 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2292 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2295 _region = add_midi_region (_view);
2296 _view->playlist()->freeze ();
2299 framepos_t const f = adjusted_current_frame (event);
2300 if (f < grab_frame()) {
2301 _region->set_position (f);
2304 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2305 so that if this region is duplicated, its duplicate starts on
2306 a snap point rather than 1 frame after a snap point. Otherwise things get
2307 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2308 place snapped notes at the start of the region.
2311 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2312 _region->set_length (len < 1 ? 1 : len);
2318 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2320 if (!movement_occurred) {
2321 add_midi_region (_view);
2323 _view->playlist()->thaw ();
2328 RegionCreateDrag::aborted (bool)
2331 _view->playlist()->thaw ();
2337 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2344 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2348 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2350 Gdk::Cursor* cursor;
2351 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2353 float x_fraction = cnote->mouse_x_fraction ();
2355 if (x_fraction > 0.0 && x_fraction < 0.25) {
2356 cursor = _editor->cursors()->left_side_trim;
2359 cursor = _editor->cursors()->right_side_trim;
2363 Drag::start_grab (event, cursor);
2365 region = &cnote->region_view();
2368 temp = region->snap_to_pixel (cnote->x0 (), true);
2369 _snap_delta = temp - cnote->x0 ();
2373 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2378 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2379 if (ms.size() > 1) {
2380 /* has to be relative, may make no sense otherwise */
2383 /* select this note; if it is already selected, preserve the existing selection,
2384 otherwise make this note the only one selected.
2386 region->note_selected (cnote, cnote->selected ());
2390 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2392 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2394 _editor->begin_reversible_command (_("resize notes"));
2396 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2397 MidiRegionSelection::iterator next;
2400 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2402 mrv->begin_resizing (at_front);
2408 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2409 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2411 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2415 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2417 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2418 if (_editor->snap_mode () != SnapOff) {
2422 if (_editor->snap_mode () == SnapOff) {
2424 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2425 if (apply_snap_delta) {
2431 if (apply_snap_delta) {
2435 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2441 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2443 if (!movement_occurred) {
2447 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2448 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2449 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2451 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2454 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2456 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2457 if (_editor->snap_mode () != SnapOff) {
2461 if (_editor->snap_mode () == SnapOff) {
2463 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2464 if (apply_snap_delta) {
2470 if (apply_snap_delta) {
2474 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2478 _editor->commit_reversible_command ();
2482 NoteResizeDrag::aborted (bool)
2484 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2485 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2486 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2488 mrv->abort_resizing ();
2493 AVDraggingView::AVDraggingView (RegionView* v)
2496 initial_position = v->region()->position ();
2499 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2502 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2505 TrackViewList empty;
2507 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2508 std::list<RegionView*> views = rs.by_layer();
2511 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2512 RegionView* rv = (*i);
2513 if (!rv->region()->video_locked()) {
2516 if (rv->region()->locked()) {
2519 _views.push_back (AVDraggingView (rv));
2524 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2526 Drag::start_grab (event);
2527 if (_editor->session() == 0) {
2531 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2537 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2541 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2542 _max_backwards_drag = (
2543 ARDOUR_UI::instance()->video_timeline->get_duration()
2544 + ARDOUR_UI::instance()->video_timeline->get_offset()
2545 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2548 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2549 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2550 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2553 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2556 Timecode::Time timecode;
2557 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2558 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);
2559 show_verbose_cursor_text (buf);
2563 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2565 if (_editor->session() == 0) {
2568 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2572 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2576 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2577 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2579 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2580 dt = - _max_backwards_drag;
2583 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2584 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2586 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2587 RegionView* rv = i->view;
2588 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2591 rv->region()->clear_changes ();
2592 rv->region()->suspend_property_changes();
2594 rv->region()->set_position(i->initial_position + dt);
2595 rv->region_changed(ARDOUR::Properties::position);
2598 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2599 Timecode::Time timecode;
2600 Timecode::Time timediff;
2602 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2603 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2604 snprintf (buf, sizeof (buf),
2605 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2606 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2607 , _("Video Start:"),
2608 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2610 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2612 show_verbose_cursor_text (buf);
2616 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2618 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2625 if (!movement_occurred || ! _editor->session()) {
2629 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2631 _editor->begin_reversible_command (_("Move Video"));
2633 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2634 ARDOUR_UI::instance()->video_timeline->save_undo();
2635 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2636 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2638 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2639 i->view->drag_end();
2640 i->view->region()->resume_property_changes ();
2642 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2645 _editor->session()->maybe_update_session_range(
2646 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2647 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2651 _editor->commit_reversible_command ();
2655 VideoTimeLineDrag::aborted (bool)
2657 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2660 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2661 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2663 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2664 i->view->region()->resume_property_changes ();
2665 i->view->region()->set_position(i->initial_position);
2669 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2670 : RegionDrag (e, i, p, v)
2671 , _preserve_fade_anchor (preserve_fade_anchor)
2672 , _jump_position_when_done (false)
2674 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2678 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2681 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2682 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2684 if (tv && tv->is_track()) {
2685 speed = tv->track()->speed();
2688 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2689 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2690 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2692 framepos_t const pf = adjusted_current_frame (event);
2693 setup_snap_delta (region_start);
2695 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2696 /* Move the contents of the region around without changing the region bounds */
2697 _operation = ContentsTrim;
2698 Drag::start_grab (event, _editor->cursors()->trimmer);
2700 /* These will get overridden for a point trim.*/
2701 if (pf < (region_start + region_length/2)) {
2702 /* closer to front */
2703 _operation = StartTrim;
2704 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2705 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2707 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2711 _operation = EndTrim;
2712 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2713 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2715 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2719 /* jump trim disabled for now
2720 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2721 _jump_position_when_done = true;
2725 switch (_operation) {
2727 show_verbose_cursor_time (region_start);
2728 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2729 i->view->trim_front_starting ();
2733 show_verbose_cursor_duration (region_start, region_end);
2736 show_verbose_cursor_time (pf);
2740 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2741 i->view->region()->suspend_property_changes ();
2746 TrimDrag::motion (GdkEvent* event, bool first_move)
2748 RegionView* rv = _primary;
2751 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2752 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2753 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2754 frameoffset_t frame_delta = 0;
2756 if (tv && tv->is_track()) {
2757 speed = tv->track()->speed();
2759 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2760 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2766 switch (_operation) {
2768 trim_type = "Region start trim";
2771 trim_type = "Region end trim";
2774 trim_type = "Region content trim";
2781 _editor->begin_reversible_command (trim_type);
2783 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2784 RegionView* rv = i->view;
2785 rv->enable_display (false);
2786 rv->region()->playlist()->clear_owned_changes ();
2788 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2791 arv->temporarily_hide_envelope ();
2795 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2796 insert_result = _editor->motion_frozen_playlists.insert (pl);
2798 if (insert_result.second) {
2804 bool non_overlap_trim = false;
2806 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2807 non_overlap_trim = true;
2810 /* contstrain trim to fade length */
2811 if (_preserve_fade_anchor) {
2812 switch (_operation) {
2814 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2815 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2817 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2818 if (ar->locked()) continue;
2819 framecnt_t len = ar->fade_in()->back()->when;
2820 if (len < dt) dt = min(dt, len);
2824 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2825 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2827 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2828 if (ar->locked()) continue;
2829 framecnt_t len = ar->fade_out()->back()->when;
2830 if (len < -dt) dt = max(dt, -len);
2839 switch (_operation) {
2841 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2842 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2843 if (changed && _preserve_fade_anchor) {
2844 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2846 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2847 framecnt_t len = ar->fade_in()->back()->when;
2848 framecnt_t diff = ar->first_frame() - i->initial_position;
2849 framepos_t new_length = len - diff;
2850 i->anchored_fade_length = min (ar->length(), new_length);
2851 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2852 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2859 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2860 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2861 if (changed && _preserve_fade_anchor) {
2862 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2864 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2865 framecnt_t len = ar->fade_out()->back()->when;
2866 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2867 framepos_t new_length = len + diff;
2868 i->anchored_fade_length = min (ar->length(), new_length);
2869 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2870 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2878 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2880 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2881 i->view->move_contents (frame_delta);
2887 switch (_operation) {
2889 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2892 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2895 // show_verbose_cursor_time (frame_delta);
2901 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2903 if (movement_occurred) {
2904 motion (event, false);
2906 if (_operation == StartTrim) {
2907 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2909 /* This must happen before the region's StatefulDiffCommand is created, as it may
2910 `correct' (ahem) the region's _start from being negative to being zero. It
2911 needs to be zero in the undo record.
2913 i->view->trim_front_ending ();
2915 if (_preserve_fade_anchor) {
2916 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2918 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2919 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2920 ar->set_fade_in_length(i->anchored_fade_length);
2921 ar->set_fade_in_active(true);
2924 if (_jump_position_when_done) {
2925 i->view->region()->set_position (i->initial_position);
2928 } else if (_operation == EndTrim) {
2929 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2930 if (_preserve_fade_anchor) {
2931 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2933 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2934 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2935 ar->set_fade_out_length(i->anchored_fade_length);
2936 ar->set_fade_out_active(true);
2939 if (_jump_position_when_done) {
2940 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2945 if (!_views.empty()) {
2946 if (_operation == StartTrim) {
2947 _editor->maybe_locate_with_edit_preroll(
2948 _views.begin()->view->region()->position());
2950 if (_operation == EndTrim) {
2951 _editor->maybe_locate_with_edit_preroll(
2952 _views.begin()->view->region()->position() +
2953 _views.begin()->view->region()->length());
2957 if (!_editor->selection->selected (_primary)) {
2958 _primary->thaw_after_trim ();
2961 set<boost::shared_ptr<Playlist> > diffed_playlists;
2963 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2964 i->view->thaw_after_trim ();
2965 i->view->enable_display (true);
2967 /* Trimming one region may affect others on the playlist, so we need
2968 to get undo Commands from the whole playlist rather than just the
2969 region. Use diffed_playlists to make sure we don't diff a given
2970 playlist more than once.
2972 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2973 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2974 vector<Command*> cmds;
2976 _editor->session()->add_commands (cmds);
2977 diffed_playlists.insert (p);
2982 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2986 _editor->motion_frozen_playlists.clear ();
2987 _editor->commit_reversible_command();
2990 /* no mouse movement */
2991 _editor->point_trim (event, adjusted_current_frame (event));
2994 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2995 if (_operation == StartTrim) {
2996 i->view->trim_front_ending ();
2999 i->view->region()->resume_property_changes ();
3004 TrimDrag::aborted (bool movement_occurred)
3006 /* Our motion method is changing model state, so use the Undo system
3007 to cancel. Perhaps not ideal, as this will leave an Undo point
3008 behind which may be slightly odd from the user's point of view.
3013 if (movement_occurred) {
3017 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3018 i->view->region()->resume_property_changes ();
3023 TrimDrag::setup_pointer_frame_offset ()
3025 list<DraggingView>::iterator i = _views.begin ();
3026 while (i != _views.end() && i->view != _primary) {
3030 if (i == _views.end()) {
3034 switch (_operation) {
3036 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3039 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3046 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3050 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3051 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3056 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3058 Drag::start_grab (event, cursor);
3059 show_verbose_cursor_time (adjusted_current_frame(event));
3063 MeterMarkerDrag::setup_pointer_frame_offset ()
3065 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3069 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3071 if (!_marker->meter().movable()) {
3077 // create a dummy marker for visual representation of moving the
3078 // section, because whether its a copy or not, we're going to
3079 // leave or lose the original marker (leave if its a copy; lose if its
3080 // not, because we'll remove it from the map).
3082 MeterSection section (_marker->meter());
3084 if (!section.movable()) {
3089 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3091 _marker = new MeterMarker (
3093 *_editor->meter_group,
3094 ARDOUR_UI::config()->color ("meter marker"),
3096 *new MeterSection (_marker->meter())
3099 /* use the new marker for the grab */
3100 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3103 TempoMap& map (_editor->session()->tempo_map());
3104 /* get current state */
3105 before_state = &map.get_state();
3106 /* remove the section while we drag it */
3107 map.remove_meter (section, true);
3111 framepos_t const pf = adjusted_current_frame (event);
3113 _marker->set_position (pf);
3114 show_verbose_cursor_time (pf);
3118 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3120 if (!movement_occurred) {
3121 if (was_double_click()) {
3122 _editor->edit_meter_marker (*_marker);
3127 if (!_marker->meter().movable()) {
3131 motion (event, false);
3133 Timecode::BBT_Time when;
3135 TempoMap& map (_editor->session()->tempo_map());
3136 map.bbt_time (last_pointer_frame(), when);
3138 if (_copy == true) {
3139 _editor->begin_reversible_command (_("copy meter mark"));
3140 XMLNode &before = map.get_state();
3141 map.add_meter (_marker->meter(), when);
3142 XMLNode &after = map.get_state();
3143 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3144 _editor->commit_reversible_command ();
3147 _editor->begin_reversible_command (_("move meter mark"));
3149 /* we removed it before, so add it back now */
3151 map.add_meter (_marker->meter(), when);
3152 XMLNode &after = map.get_state();
3153 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3154 _editor->commit_reversible_command ();
3157 // delete the dummy marker we used for visual representation while moving.
3158 // a new visual marker will show up automatically.
3163 MeterMarkerDrag::aborted (bool moved)
3165 _marker->set_position (_marker->meter().frame ());
3168 TempoMap& map (_editor->session()->tempo_map());
3169 /* we removed it before, so add it back now */
3170 map.add_meter (_marker->meter(), _marker->meter().frame());
3171 // delete the dummy marker we used for visual representation while moving.
3172 // a new visual marker will show up automatically.
3177 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3181 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3183 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3188 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3190 Drag::start_grab (event, cursor);
3191 show_verbose_cursor_time (adjusted_current_frame (event));
3195 TempoMarkerDrag::setup_pointer_frame_offset ()
3197 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3201 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3203 if (!_marker->tempo().movable()) {
3209 // create a dummy marker for visual representation of moving the
3210 // section, because whether its a copy or not, we're going to
3211 // leave or lose the original marker (leave if its a copy; lose if its
3212 // not, because we'll remove it from the map).
3214 // create a dummy marker for visual representation of moving the copy.
3215 // The actual copying is not done before we reach the finish callback.
3218 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3220 TempoSection section (_marker->tempo());
3222 _marker = new TempoMarker (
3224 *_editor->tempo_group,
3225 ARDOUR_UI::config()->color ("tempo marker"),
3227 *new TempoSection (_marker->tempo())
3230 /* use the new marker for the grab */
3231 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3234 TempoMap& map (_editor->session()->tempo_map());
3235 /* get current state */
3236 before_state = &map.get_state();
3237 /* remove the section while we drag it */
3238 map.remove_tempo (section, true);
3242 framepos_t const pf = adjusted_current_frame (event);
3243 _marker->set_position (pf);
3244 show_verbose_cursor_time (pf);
3248 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3250 if (!movement_occurred) {
3251 if (was_double_click()) {
3252 _editor->edit_tempo_marker (*_marker);
3257 if (!_marker->tempo().movable()) {
3261 motion (event, false);
3263 TempoMap& map (_editor->session()->tempo_map());
3264 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3265 Timecode::BBT_Time when;
3267 map.bbt_time (beat_time, when);
3269 if (_copy == true) {
3270 _editor->begin_reversible_command (_("copy tempo mark"));
3271 XMLNode &before = map.get_state();
3272 map.add_tempo (_marker->tempo(), when);
3273 XMLNode &after = map.get_state();
3274 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3275 _editor->commit_reversible_command ();
3278 _editor->begin_reversible_command (_("move tempo mark"));
3279 /* we removed it before, so add it back now */
3280 map.add_tempo (_marker->tempo(), when);
3281 XMLNode &after = map.get_state();
3282 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3283 _editor->commit_reversible_command ();
3286 // delete the dummy marker we used for visual representation while moving.
3287 // a new visual marker will show up automatically.
3292 TempoMarkerDrag::aborted (bool moved)
3294 _marker->set_position (_marker->tempo().frame());
3296 TempoMap& map (_editor->session()->tempo_map());
3297 /* we removed it before, so add it back now */
3298 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3299 // delete the dummy marker we used for visual representation while moving.
3300 // a new visual marker will show up automatically.
3305 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3306 : Drag (e, &c.track_canvas_item(), false)
3310 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3313 /** Do all the things we do when dragging the playhead to make it look as though
3314 * we have located, without actually doing the locate (because that would cause
3315 * the diskstream buffers to be refilled, which is too slow).
3318 CursorDrag::fake_locate (framepos_t t)
3320 if (_editor->session () == 0) {
3324 _editor->playhead_cursor->set_position (t);
3326 Session* s = _editor->session ();
3327 if (s->timecode_transmission_suspended ()) {
3328 framepos_t const f = _editor->playhead_cursor->current_frame ();
3329 /* This is asynchronous so it will be sent "now"
3331 s->send_mmc_locate (f);
3332 /* These are synchronous and will be sent during the next
3335 s->queue_full_time_code ();
3336 s->queue_song_position_pointer ();
3339 show_verbose_cursor_time (t);
3340 _editor->UpdateAllTransportClocks (t);
3344 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3346 Drag::start_grab (event, c);
3347 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3349 _grab_zoom = _editor->samples_per_pixel;
3351 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3353 _editor->snap_to_with_modifier (where, event);
3355 _editor->_dragging_playhead = true;
3357 Session* s = _editor->session ();
3359 /* grab the track canvas item as well */
3361 _cursor.track_canvas_item().grab();
3364 if (_was_rolling && _stop) {
3368 if (s->is_auditioning()) {
3369 s->cancel_audition ();
3373 if (AudioEngine::instance()->connected()) {
3375 /* do this only if we're the engine is connected
3376 * because otherwise this request will never be
3377 * serviced and we'll busy wait forever. likewise,
3378 * notice if we are disconnected while waiting for the
3379 * request to be serviced.
3382 s->request_suspend_timecode_transmission ();
3383 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3384 /* twiddle our thumbs */
3389 fake_locate (where - snap_delta (event->button.state));
3393 CursorDrag::motion (GdkEvent* event, bool)
3395 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3396 _editor->snap_to_with_modifier (where, event);
3397 if (where != last_pointer_frame()) {
3398 fake_locate (where - snap_delta (event->button.state));
3403 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3405 _editor->_dragging_playhead = false;
3407 _cursor.track_canvas_item().ungrab();
3409 if (!movement_occurred && _stop) {
3413 motion (event, false);
3415 Session* s = _editor->session ();
3417 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3418 _editor->_pending_locate_request = true;
3419 s->request_resume_timecode_transmission ();
3424 CursorDrag::aborted (bool)
3426 _cursor.track_canvas_item().ungrab();
3428 if (_editor->_dragging_playhead) {
3429 _editor->session()->request_resume_timecode_transmission ();
3430 _editor->_dragging_playhead = false;
3433 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3436 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3437 : RegionDrag (e, i, p, v)
3439 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3443 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3445 Drag::start_grab (event, cursor);
3447 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3448 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3449 setup_snap_delta (r->position ());
3451 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3455 FadeInDrag::setup_pointer_frame_offset ()
3457 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3458 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3459 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3463 FadeInDrag::motion (GdkEvent* event, bool)
3465 framecnt_t fade_length;
3467 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3468 _editor->snap_to_with_modifier (pos, event);
3469 pos -= snap_delta (event->button.state);
3471 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3473 if (pos < (region->position() + 64)) {
3474 fade_length = 64; // this should be a minimum defined somewhere
3475 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3476 fade_length = region->length() - region->fade_out()->back()->when - 1;
3478 fade_length = pos - region->position();
3481 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3483 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3489 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3492 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3496 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3498 if (!movement_occurred) {
3502 framecnt_t fade_length;
3503 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3504 _editor->snap_to_with_modifier (pos, event);
3505 pos -= snap_delta (event->button.state);
3507 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3509 if (pos < (region->position() + 64)) {
3510 fade_length = 64; // this should be a minimum defined somewhere
3511 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3512 fade_length = region->length() - region->fade_out()->back()->when - 1;
3514 fade_length = pos - region->position();
3517 bool in_command = false;
3519 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3521 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3527 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3528 XMLNode &before = alist->get_state();
3530 tmp->audio_region()->set_fade_in_length (fade_length);
3531 tmp->audio_region()->set_fade_in_active (true);
3534 _editor->begin_reversible_command (_("change fade in length"));
3537 XMLNode &after = alist->get_state();
3538 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3542 _editor->commit_reversible_command ();
3547 FadeInDrag::aborted (bool)
3549 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3550 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3556 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3560 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3561 : RegionDrag (e, i, p, v)
3563 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3567 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3569 Drag::start_grab (event, cursor);
3571 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3572 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3573 setup_snap_delta (r->last_frame ());
3575 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3579 FadeOutDrag::setup_pointer_frame_offset ()
3581 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3582 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3583 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3587 FadeOutDrag::motion (GdkEvent* event, bool)
3589 framecnt_t fade_length;
3591 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3592 _editor->snap_to_with_modifier (pos, event);
3593 pos -= snap_delta (event->button.state);
3595 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3597 if (pos > (region->last_frame() - 64)) {
3598 fade_length = 64; // this should really be a minimum fade defined somewhere
3599 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3600 fade_length = region->length() - region->fade_in()->back()->when - 1;
3602 fade_length = region->last_frame() - pos;
3605 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3607 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3613 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3616 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3620 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3622 if (!movement_occurred) {
3626 framecnt_t fade_length;
3628 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3629 _editor->snap_to_with_modifier (pos, event);
3630 pos -= snap_delta (event->button.state);
3632 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3634 if (pos > (region->last_frame() - 64)) {
3635 fade_length = 64; // this should really be a minimum fade defined somewhere
3636 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3637 fade_length = region->length() - region->fade_in()->back()->when - 1;
3639 fade_length = region->last_frame() - pos;
3642 bool in_command = false;
3644 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3646 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3652 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3653 XMLNode &before = alist->get_state();
3655 tmp->audio_region()->set_fade_out_length (fade_length);
3656 tmp->audio_region()->set_fade_out_active (true);
3659 _editor->begin_reversible_command (_("change fade out length"));
3662 XMLNode &after = alist->get_state();
3663 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3667 _editor->commit_reversible_command ();
3672 FadeOutDrag::aborted (bool)
3674 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3675 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3681 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3685 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3688 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3690 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3693 _points.push_back (ArdourCanvas::Duple (0, 0));
3694 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3697 MarkerDrag::~MarkerDrag ()
3699 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3704 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3706 location = new Location (*l);
3707 markers.push_back (m);
3712 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3714 Drag::start_grab (event, cursor);
3718 Location *location = _editor->find_location_from_marker (_marker, is_start);
3719 _editor->_dragging_edit_point = true;
3721 update_item (location);
3723 // _drag_line->show();
3724 // _line->raise_to_top();
3727 show_verbose_cursor_time (location->start());
3729 show_verbose_cursor_time (location->end());
3732 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3735 case Selection::Toggle:
3736 /* we toggle on the button release */
3738 case Selection::Set:
3739 if (!_editor->selection->selected (_marker)) {
3740 _editor->selection->set (_marker);
3743 case Selection::Extend:
3745 Locations::LocationList ll;
3746 list<ArdourMarker*> to_add;
3748 _editor->selection->markers.range (s, e);
3749 s = min (_marker->position(), s);
3750 e = max (_marker->position(), e);
3753 if (e < max_framepos) {
3756 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3757 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3758 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3761 to_add.push_back (lm->start);
3764 to_add.push_back (lm->end);
3768 if (!to_add.empty()) {
3769 _editor->selection->add (to_add);
3773 case Selection::Add:
3774 _editor->selection->add (_marker);
3778 /* Set up copies for us to manipulate during the drag
3781 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3783 Location* l = _editor->find_location_from_marker (*i, is_start);
3790 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3792 /* range: check that the other end of the range isn't
3795 CopiedLocationInfo::iterator x;
3796 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3797 if (*(*x).location == *l) {
3801 if (x == _copied_locations.end()) {
3802 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3804 (*x).markers.push_back (*i);
3805 (*x).move_both = true;
3813 MarkerDrag::setup_pointer_frame_offset ()
3816 Location *location = _editor->find_location_from_marker (_marker, is_start);
3817 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3821 MarkerDrag::motion (GdkEvent* event, bool)
3823 framecnt_t f_delta = 0;
3825 bool move_both = false;
3826 Location *real_location;
3827 Location *copy_location = 0;
3829 framepos_t const newframe = adjusted_current_frame (event);
3830 framepos_t next = newframe;
3832 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
3836 CopiedLocationInfo::iterator x;
3838 /* find the marker we're dragging, and compute the delta */
3840 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3842 copy_location = (*x).location;
3844 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3846 /* this marker is represented by this
3847 * CopiedLocationMarkerInfo
3850 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3855 if (real_location->is_mark()) {
3856 f_delta = newframe - copy_location->start();
3860 switch (_marker->type()) {
3861 case ArdourMarker::SessionStart:
3862 case ArdourMarker::RangeStart:
3863 case ArdourMarker::LoopStart:
3864 case ArdourMarker::PunchIn:
3865 f_delta = newframe - copy_location->start();
3868 case ArdourMarker::SessionEnd:
3869 case ArdourMarker::RangeEnd:
3870 case ArdourMarker::LoopEnd:
3871 case ArdourMarker::PunchOut:
3872 f_delta = newframe - copy_location->end();
3875 /* what kind of marker is this ? */
3884 if (x == _copied_locations.end()) {
3885 /* hmm, impossible - we didn't find the dragged marker */
3889 /* now move them all */
3891 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3893 copy_location = x->location;
3895 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3899 if (real_location->locked()) {
3903 if (copy_location->is_mark()) {
3907 copy_location->set_start (copy_location->start() + f_delta);
3911 framepos_t new_start = copy_location->start() + f_delta;
3912 framepos_t new_end = copy_location->end() + f_delta;
3914 if (is_start) { // start-of-range marker
3916 if (move_both || (*x).move_both) {
3917 copy_location->set_start (new_start);
3918 copy_location->set_end (new_end);
3919 } else if (new_start < copy_location->end()) {
3920 copy_location->set_start (new_start);
3921 } else if (newframe > 0) {
3922 _editor->snap_to (next, RoundUpAlways, true);
3923 copy_location->set_end (next);
3924 copy_location->set_start (newframe);
3927 } else { // end marker
3929 if (move_both || (*x).move_both) {
3930 copy_location->set_end (new_end);
3931 copy_location->set_start (new_start);
3932 } else if (new_end > copy_location->start()) {
3933 copy_location->set_end (new_end);
3934 } else if (newframe > 0) {
3935 _editor->snap_to (next, RoundDownAlways, true);
3936 copy_location->set_start (next);
3937 copy_location->set_end (newframe);
3942 update_item (copy_location);
3944 /* now lookup the actual GUI items used to display this
3945 * location and move them to wherever the copy of the location
3946 * is now. This means that the logic in ARDOUR::Location is
3947 * still enforced, even though we are not (yet) modifying
3948 * the real Location itself.
3951 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3954 lm->set_position (copy_location->start(), copy_location->end());
3959 assert (!_copied_locations.empty());
3961 show_verbose_cursor_time (newframe);
3965 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3967 if (!movement_occurred) {
3969 if (was_double_click()) {
3970 _editor->rename_marker (_marker);
3974 /* just a click, do nothing but finish
3975 off the selection process
3978 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3981 case Selection::Set:
3982 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3983 _editor->selection->set (_marker);
3987 case Selection::Toggle:
3988 /* we toggle on the button release, click only */
3989 _editor->selection->toggle (_marker);
3992 case Selection::Extend:
3993 case Selection::Add:
4000 _editor->_dragging_edit_point = false;
4002 XMLNode &before = _editor->session()->locations()->get_state();
4003 bool in_command = false;
4005 MarkerSelection::iterator i;
4006 CopiedLocationInfo::iterator x;
4009 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4010 x != _copied_locations.end() && i != _editor->selection->markers.end();
4013 Location * location = _editor->find_location_from_marker (*i, is_start);
4017 if (location->locked()) {
4021 _editor->begin_reversible_command ( _("move marker") );
4024 if (location->is_mark()) {
4025 location->set_start (((*x).location)->start());
4027 location->set (((*x).location)->start(), ((*x).location)->end());
4033 XMLNode &after = _editor->session()->locations()->get_state();
4034 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4035 _editor->commit_reversible_command ();
4040 MarkerDrag::aborted (bool movement_occured)
4042 if (!movement_occured) {
4046 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4048 /* move all markers to their original location */
4051 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4054 Location * location = _editor->find_location_from_marker (*m, is_start);
4057 (*m)->set_position (is_start ? location->start() : location->end());
4064 MarkerDrag::update_item (Location*)
4069 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4071 _cumulative_x_drag (0)
4072 , _cumulative_y_drag (0)
4075 if (_zero_gain_fraction < 0.0) {
4076 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4079 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4081 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4087 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4089 Drag::start_grab (event, _editor->cursors()->fader);
4091 // start the grab at the center of the control point so
4092 // the point doesn't 'jump' to the mouse after the first drag
4093 _fixed_grab_x = _point->get_x();
4094 _fixed_grab_y = _point->get_y();
4096 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4097 setup_snap_delta (pos);
4099 float const fraction = 1 - (_point->get_y() / _point->line().height());
4100 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4102 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4104 if (!_point->can_slide ()) {
4105 _x_constrained = true;
4110 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4112 double dx = _drags->current_pointer_x() - last_pointer_x();
4113 double dy = current_pointer_y() - last_pointer_y();
4115 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4120 /* coordinate in pixels relative to the start of the region (for region-based automation)
4121 or track (for track-based automation) */
4122 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4123 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4125 // calculate zero crossing point. back off by .01 to stay on the
4126 // positive side of zero
4127 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4129 if (_x_constrained) {
4132 if (_y_constrained) {
4136 _cumulative_x_drag = cx - _fixed_grab_x;
4137 _cumulative_y_drag = cy - _fixed_grab_y;
4139 // make sure we hit zero when passing through
4140 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4146 cy = min ((double) _point->line().height(), cy);
4148 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4150 if (!_x_constrained) {
4151 _editor->snap_to_with_modifier (cx_frames, event);
4154 cx_frames -= snap_delta (event->button.state);
4155 cx_frames = min (cx_frames, _point->line().maximum_time());
4157 float const fraction = 1.0 - (cy / _point->line().height());
4160 _editor->begin_reversible_command (_("automation event move"));
4161 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
4164 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4166 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4170 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4172 if (!movement_occurred) {
4175 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4176 _editor->reset_point_selection ();
4180 motion (event, false);
4181 _point->line().end_drag (_pushing, _final_index);
4182 _editor->commit_reversible_command ();
4187 ControlPointDrag::aborted (bool)
4189 _point->line().reset ();
4193 ControlPointDrag::active (Editing::MouseMode m)
4195 if (m == Editing::MouseDraw) {
4196 /* always active in mouse draw */
4200 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4201 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4204 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4207 , _cumulative_y_drag (0)
4211 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4215 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4217 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4220 _item = &_line->grab_item ();
4222 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4223 origin, and ditto for y.
4226 double cx = event->button.x;
4227 double cy = event->button.y;
4229 _line->parent_group().canvas_to_item (cx, cy);
4231 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4233 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4234 /* no adjacent points */
4238 Drag::start_grab (event, _editor->cursors()->fader);
4240 /* store grab start in parent frame */
4245 double fraction = 1.0 - (cy / _line->height());
4247 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4251 LineDrag::motion (GdkEvent* event, bool first_move)
4253 double dy = current_pointer_y() - last_pointer_y();
4255 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4259 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4261 _cumulative_y_drag = cy - _fixed_grab_y;
4264 cy = min ((double) _line->height(), cy);
4266 double const fraction = 1.0 - (cy / _line->height());
4270 _editor->begin_reversible_command (_("automation range move"));
4271 _line->start_drag_line (_before, _after, fraction);
4274 /* we are ignoring x position for this drag, so we can just pass in anything */
4275 _line->drag_motion (0, fraction, true, false, ignored);
4277 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4281 LineDrag::finished (GdkEvent* event, bool movement_occured)
4283 if (movement_occured) {
4284 motion (event, false);
4285 _line->end_drag (false, 0);
4286 _editor->commit_reversible_command ();
4288 /* add a new control point on the line */
4290 AutomationTimeAxisView* atv;
4292 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4293 framepos_t where = _editor->canvas_event_sample (event, 0, 0);
4295 atv->add_automation_event (event, where, event->button.y, false);
4296 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4297 AudioRegionView* arv;
4299 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4300 arv->add_gain_point_event (arv->get_canvas_group (), event, false);
4307 LineDrag::aborted (bool)
4312 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4315 _cumulative_x_drag (0)
4317 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4321 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4323 Drag::start_grab (event);
4325 _line = reinterpret_cast<Line*> (_item);
4328 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4330 double cx = event->button.x;
4331 double cy = event->button.y;
4333 _item->parent()->canvas_to_item (cx, cy);
4335 /* store grab start in parent frame */
4336 _region_view_grab_x = cx;
4338 _before = *(float*) _item->get_data ("position");
4340 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4342 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4346 FeatureLineDrag::motion (GdkEvent*, bool)
4348 double dx = _drags->current_pointer_x() - last_pointer_x();
4350 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4352 _cumulative_x_drag += dx;
4354 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4363 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4365 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4367 float *pos = new float;
4370 _line->set_data ("position", pos);
4376 FeatureLineDrag::finished (GdkEvent*, bool)
4378 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4379 _arv->update_transient(_before, _before);
4383 FeatureLineDrag::aborted (bool)
4388 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4390 , _vertical_only (false)
4392 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4396 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4398 Drag::start_grab (event);
4399 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4403 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4410 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4412 framepos_t grab = grab_frame ();
4413 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4414 _editor->snap_to_with_modifier (grab, event);
4416 grab = raw_grab_frame ();
4419 /* base start and end on initial click position */
4429 if (current_pointer_y() < grab_y()) {
4430 y1 = current_pointer_y();
4433 y2 = current_pointer_y();
4437 if (start != end || y1 != y2) {
4439 double x1 = _editor->sample_to_pixel (start);
4440 double x2 = _editor->sample_to_pixel (end);
4441 const double min_dimension = 2.0;
4443 if (_vertical_only) {
4444 /* fixed 10 pixel width */
4448 x2 = min (x1 - min_dimension, x2);
4450 x2 = max (x1 + min_dimension, x2);
4455 y2 = min (y1 - min_dimension, y2);
4457 y2 = max (y1 + min_dimension, y2);
4460 /* translate rect into item space and set */
4462 ArdourCanvas::Rect r (x1, y1, x2, y2);
4464 /* this drag is a _trackview_only == true drag, so the y1 and
4465 * y2 (computed using current_pointer_y() and grab_y()) will be
4466 * relative to the top of the trackview group). The
4467 * rubberband rect has the same parent/scroll offset as the
4468 * the trackview group, so we can use the "r" rect directly
4469 * to set the shape of the rubberband.
4472 _editor->rubberband_rect->set (r);
4473 _editor->rubberband_rect->show();
4474 _editor->rubberband_rect->raise_to_top();
4476 show_verbose_cursor_time (pf);
4478 do_select_things (event, true);
4483 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4487 framepos_t grab = grab_frame ();
4488 framepos_t lpf = last_pointer_frame ();
4490 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4491 grab = raw_grab_frame ();
4492 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4506 if (current_pointer_y() < grab_y()) {
4507 y1 = current_pointer_y();
4510 y2 = current_pointer_y();
4514 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4518 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4520 if (movement_occurred) {
4522 motion (event, false);
4523 do_select_things (event, false);
4529 bool do_deselect = true;
4530 MidiTimeAxisView* mtv;
4532 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4534 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4535 /* nothing selected */
4536 add_midi_region (mtv);
4537 do_deselect = false;
4541 /* do not deselect if Primary or Tertiary (toggle-select or
4542 * extend-select are pressed.
4545 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4546 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4553 _editor->rubberband_rect->hide();
4557 RubberbandSelectDrag::aborted (bool)
4559 _editor->rubberband_rect->hide ();
4562 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4563 : RegionDrag (e, i, p, v)
4565 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4569 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4571 Drag::start_grab (event, cursor);
4573 _editor->get_selection().add (_primary);
4575 framepos_t where = _primary->region()->position();
4576 setup_snap_delta (where);
4578 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4582 TimeFXDrag::motion (GdkEvent* event, bool)
4584 RegionView* rv = _primary;
4585 StreamView* cv = rv->get_time_axis_view().view ();
4587 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4588 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4589 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4590 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4591 _editor->snap_to_with_modifier (pf, event);
4592 pf -= snap_delta (event->button.state);
4594 if (pf > rv->region()->position()) {
4595 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4598 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4602 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4604 _primary->get_time_axis_view().hide_timestretch ();
4606 if (!movement_occurred) {
4610 if (last_pointer_frame() < _primary->region()->position()) {
4611 /* backwards drag of the left edge - not usable */
4615 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4617 float percentage = (double) newlen / (double) _primary->region()->length();
4619 #ifndef USE_RUBBERBAND
4620 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4621 if (_primary->region()->data_type() == DataType::AUDIO) {
4622 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4626 if (!_editor->get_selection().regions.empty()) {
4627 /* primary will already be included in the selection, and edit
4628 group shared editing will propagate selection across
4629 equivalent regions, so just use the current region
4633 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4634 error << _("An error occurred while executing time stretch operation") << endmsg;
4640 TimeFXDrag::aborted (bool)
4642 _primary->get_time_axis_view().hide_timestretch ();
4645 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4648 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4652 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4654 Drag::start_grab (event);
4658 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4660 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4664 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4666 if (movement_occurred && _editor->session()) {
4667 /* make sure we stop */
4668 _editor->session()->request_transport_speed (0.0);
4673 ScrubDrag::aborted (bool)
4678 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4682 , _time_selection_at_start (!_editor->get_selection().time.empty())
4684 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4686 if (_time_selection_at_start) {
4687 start_at_start = _editor->get_selection().time.start();
4688 end_at_start = _editor->get_selection().time.end_frame();
4693 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4695 if (_editor->session() == 0) {
4699 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4701 switch (_operation) {
4702 case CreateSelection:
4703 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4708 cursor = _editor->cursors()->selector;
4709 Drag::start_grab (event, cursor);
4712 case SelectionStartTrim:
4713 if (_editor->clicked_axisview) {
4714 _editor->clicked_axisview->order_selection_trims (_item, true);
4716 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4719 case SelectionEndTrim:
4720 if (_editor->clicked_axisview) {
4721 _editor->clicked_axisview->order_selection_trims (_item, false);
4723 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4727 Drag::start_grab (event, cursor);
4730 case SelectionExtend:
4731 Drag::start_grab (event, cursor);
4735 if (_operation == SelectionMove) {
4736 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4738 show_verbose_cursor_time (adjusted_current_frame (event));
4743 SelectionDrag::setup_pointer_frame_offset ()
4745 switch (_operation) {
4746 case CreateSelection:
4747 _pointer_frame_offset = 0;
4750 case SelectionStartTrim:
4752 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4755 case SelectionEndTrim:
4756 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4759 case SelectionExtend:
4765 SelectionDrag::motion (GdkEvent* event, bool first_move)
4767 framepos_t start = 0;
4769 framecnt_t length = 0;
4770 framecnt_t distance = 0;
4772 framepos_t const pending_position = adjusted_current_frame (event);
4774 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4778 switch (_operation) {
4779 case CreateSelection:
4781 framepos_t grab = grab_frame ();
4784 grab = adjusted_current_frame (event, false);
4785 if (grab < pending_position) {
4786 _editor->snap_to (grab, RoundDownMaybe);
4788 _editor->snap_to (grab, RoundUpMaybe);
4792 if (pending_position < grab) {
4793 start = pending_position;
4796 end = pending_position;
4800 /* first drag: Either add to the selection
4801 or create a new selection
4808 /* adding to the selection */
4809 _editor->set_selected_track_as_side_effect (Selection::Add);
4810 _editor->clicked_selection = _editor->selection->add (start, end);
4817 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4818 _editor->set_selected_track_as_side_effect (Selection::Set);
4821 _editor->clicked_selection = _editor->selection->set (start, end);
4825 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4826 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4827 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4829 _editor->selection->add (atest);
4833 /* select all tracks within the rectangle that we've marked out so far */
4834 TrackViewList new_selection;
4835 TrackViewList& all_tracks (_editor->track_views);
4837 ArdourCanvas::Coord const top = grab_y();
4838 ArdourCanvas::Coord const bottom = current_pointer_y();
4840 if (top >= 0 && bottom >= 0) {
4842 //first, find the tracks that are covered in the y range selection
4843 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4844 if ((*i)->covered_by_y_range (top, bottom)) {
4845 new_selection.push_back (*i);
4849 //now find any tracks that are GROUPED with the tracks we selected
4850 TrackViewList grouped_add = new_selection;
4851 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4852 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4853 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4854 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4855 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4856 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4857 grouped_add.push_back (*j);
4862 //now compare our list with the current selection, and add or remove as necessary
4863 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4864 TrackViewList tracks_to_add;
4865 TrackViewList tracks_to_remove;
4866 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4867 if ( !_editor->selection->tracks.contains ( *i ) )
4868 tracks_to_add.push_back ( *i );
4869 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4870 if ( !grouped_add.contains ( *i ) )
4871 tracks_to_remove.push_back ( *i );
4872 _editor->selection->add(tracks_to_add);
4873 _editor->selection->remove(tracks_to_remove);
4879 case SelectionStartTrim:
4881 end = _editor->selection->time[_editor->clicked_selection].end;
4883 if (pending_position > end) {
4886 start = pending_position;
4890 case SelectionEndTrim:
4892 start = _editor->selection->time[_editor->clicked_selection].start;
4894 if (pending_position < start) {
4897 end = pending_position;
4904 start = _editor->selection->time[_editor->clicked_selection].start;
4905 end = _editor->selection->time[_editor->clicked_selection].end;
4907 length = end - start;
4908 distance = pending_position - start;
4909 start = pending_position;
4910 _editor->snap_to (start);
4912 end = start + length;
4916 case SelectionExtend:
4921 switch (_operation) {
4923 if (_time_selection_at_start) {
4924 _editor->selection->move_time (distance);
4928 _editor->selection->replace (_editor->clicked_selection, start, end);
4932 if (_operation == SelectionMove) {
4933 show_verbose_cursor_time(start);
4935 show_verbose_cursor_time(pending_position);
4940 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4942 Session* s = _editor->session();
4944 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4945 if (movement_occurred) {
4946 motion (event, false);
4947 /* XXX this is not object-oriented programming at all. ick */
4948 if (_editor->selection->time.consolidate()) {
4949 _editor->selection->TimeChanged ();
4952 /* XXX what if its a music time selection? */
4954 if (s->get_play_range() && s->transport_rolling()) {
4955 s->request_play_range (&_editor->selection->time, true);
4957 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4958 if (_operation == SelectionEndTrim)
4959 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4961 s->request_locate (_editor->get_selection().time.start());
4965 if (_editor->get_selection().time.length() != 0) {
4966 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
4968 s->clear_range_selection ();
4973 /* just a click, no pointer movement.
4976 if (_operation == SelectionExtend) {
4977 if (_time_selection_at_start) {
4978 framepos_t pos = adjusted_current_frame (event, false);
4979 framepos_t start = min (pos, start_at_start);
4980 framepos_t end = max (pos, end_at_start);
4981 _editor->selection->set (start, end);
4984 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4985 if (_editor->clicked_selection) {
4986 _editor->selection->remove (_editor->clicked_selection);
4989 if (!_editor->clicked_selection) {
4990 _editor->selection->clear_time();
4995 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4996 _editor->selection->set (_editor->clicked_axisview);
4999 if (s && s->get_play_range () && s->transport_rolling()) {
5000 s->request_stop (false, false);
5005 _editor->stop_canvas_autoscroll ();
5006 _editor->clicked_selection = 0;
5007 _editor->commit_reversible_selection_op ();
5011 SelectionDrag::aborted (bool)
5016 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5017 : Drag (e, i, false),
5021 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5023 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5024 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5025 physical_screen_height (_editor->get_window())));
5026 _drag_rect->hide ();
5028 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
5029 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
5032 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5034 /* normal canvas items will be cleaned up when their parent group is deleted. But
5035 this item is created as the child of a long-lived parent group, and so we
5036 need to explicitly delete it.
5042 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5044 if (_editor->session() == 0) {
5048 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5050 if (!_editor->temp_location) {
5051 _editor->temp_location = new Location (*_editor->session());
5054 switch (_operation) {
5055 case CreateSkipMarker:
5056 case CreateRangeMarker:
5057 case CreateTransportMarker:
5058 case CreateCDMarker:
5060 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5065 cursor = _editor->cursors()->selector;
5069 Drag::start_grab (event, cursor);
5071 show_verbose_cursor_time (adjusted_current_frame (event));
5075 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5077 framepos_t start = 0;
5079 ArdourCanvas::Rectangle *crect;
5081 switch (_operation) {
5082 case CreateSkipMarker:
5083 crect = _editor->range_bar_drag_rect;
5085 case CreateRangeMarker:
5086 crect = _editor->range_bar_drag_rect;
5088 case CreateTransportMarker:
5089 crect = _editor->transport_bar_drag_rect;
5091 case CreateCDMarker:
5092 crect = _editor->cd_marker_bar_drag_rect;
5095 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5100 framepos_t const pf = adjusted_current_frame (event);
5102 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5103 framepos_t grab = grab_frame ();
5104 _editor->snap_to (grab);
5106 if (pf < grab_frame()) {
5114 /* first drag: Either add to the selection
5115 or create a new selection.
5120 _editor->temp_location->set (start, end);
5124 update_item (_editor->temp_location);
5126 //_drag_rect->raise_to_top();
5132 _editor->temp_location->set (start, end);
5134 double x1 = _editor->sample_to_pixel (start);
5135 double x2 = _editor->sample_to_pixel (end);
5139 update_item (_editor->temp_location);
5142 show_verbose_cursor_time (pf);
5147 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5149 Location * newloc = 0;
5153 if (movement_occurred) {
5154 motion (event, false);
5157 switch (_operation) {
5158 case CreateSkipMarker:
5159 case CreateRangeMarker:
5160 case CreateCDMarker:
5162 XMLNode &before = _editor->session()->locations()->get_state();
5163 if (_operation == CreateSkipMarker) {
5164 _editor->begin_reversible_command (_("new skip marker"));
5165 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5166 flags = Location::IsRangeMarker | Location::IsSkip;
5167 _editor->range_bar_drag_rect->hide();
5168 } else if (_operation == CreateCDMarker) {
5169 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5170 _editor->begin_reversible_command (_("new CD marker"));
5171 flags = Location::IsRangeMarker | Location::IsCDMarker;
5172 _editor->cd_marker_bar_drag_rect->hide();
5174 _editor->begin_reversible_command (_("new skip marker"));
5175 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5176 flags = Location::IsRangeMarker;
5177 _editor->range_bar_drag_rect->hide();
5179 newloc = new Location (
5180 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5183 _editor->session()->locations()->add (newloc, true);
5184 XMLNode &after = _editor->session()->locations()->get_state();
5185 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5186 _editor->commit_reversible_command ();
5190 case CreateTransportMarker:
5191 // popup menu to pick loop or punch
5192 _editor->new_transport_marker_context_menu (&event->button, _item);
5198 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5200 if (_operation == CreateTransportMarker) {
5202 /* didn't drag, so just locate */
5204 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5206 } else if (_operation == CreateCDMarker) {
5208 /* didn't drag, but mark is already created so do
5211 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5216 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5218 if (end == max_framepos) {
5219 end = _editor->session()->current_end_frame ();
5222 if (start == max_framepos) {
5223 start = _editor->session()->current_start_frame ();
5226 switch (_editor->mouse_mode) {
5228 /* find the two markers on either side and then make the selection from it */
5229 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5233 /* find the two markers on either side of the click and make the range out of it */
5234 _editor->selection->set (start, end);
5243 _editor->stop_canvas_autoscroll ();
5247 RangeMarkerBarDrag::aborted (bool movement_occured)
5249 if (movement_occured) {
5250 _drag_rect->hide ();
5255 RangeMarkerBarDrag::update_item (Location* location)
5257 double const x1 = _editor->sample_to_pixel (location->start());
5258 double const x2 = _editor->sample_to_pixel (location->end());
5260 _drag_rect->set_x0 (x1);
5261 _drag_rect->set_x1 (x2);
5264 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5266 , _cumulative_dx (0)
5267 , _cumulative_dy (0)
5269 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5271 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5273 _region = &_primary->region_view ();
5274 _note_height = _region->midi_stream_view()->note_height ();
5278 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5280 Drag::start_grab (event);
5281 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5283 if (!(_was_selected = _primary->selected())) {
5285 /* tertiary-click means extend selection - we'll do that on button release,
5286 so don't add it here, because otherwise we make it hard to figure
5287 out the "extend-to" range.
5290 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5293 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5296 _region->note_selected (_primary, true);
5298 _region->unique_select (_primary);
5301 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5302 _editor->commit_reversible_selection_op();
5307 /** @return Current total drag x change in frames */
5309 NoteDrag::total_dx (const guint state) const
5312 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5314 /* primary note time */
5315 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5317 /* new time of the primary note in session frames */
5318 frameoffset_t st = n + dx + snap_delta (state);
5320 framepos_t const rp = _region->region()->position ();
5322 /* prevent the note being dragged earlier than the region's position */
5325 /* possibly snap and return corresponding delta */
5329 if (ArdourKeyboard::indicates_snap (state)) {
5330 if (_editor->snap_mode () != SnapOff) {
5334 if (_editor->snap_mode () == SnapOff) {
5336 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5337 if (ArdourKeyboard::indicates_snap_delta (state)) {
5345 ret = _region->snap_frame_to_frame (st - rp) + rp - n - snap_delta (state);
5347 ret = st - n - snap_delta (state);
5352 /** @return Current total drag y change in note number */
5354 NoteDrag::total_dy () const
5356 MidiStreamView* msv = _region->midi_stream_view ();
5357 double const y = _region->midi_view()->y_position ();
5358 /* new current note */
5359 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5361 n = max (msv->lowest_note(), n);
5362 n = min (msv->highest_note(), n);
5363 /* and work out delta */
5364 return n - msv->y_to_note (grab_y() - y);
5368 NoteDrag::motion (GdkEvent * event, bool)
5370 /* Total change in x and y since the start of the drag */
5371 frameoffset_t const dx = total_dx (event->button.state);
5372 int8_t const dy = total_dy ();
5374 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5375 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5376 double const tdy = -dy * _note_height - _cumulative_dy;
5379 _cumulative_dx += tdx;
5380 _cumulative_dy += tdy;
5382 int8_t note_delta = total_dy();
5384 _region->move_selection (tdx, tdy, note_delta);
5386 /* the new note value may be the same as the old one, but we
5387 * don't know what that means because the selection may have
5388 * involved more than one note and we might be doing something
5389 * odd with them. so show the note value anyway, always.
5393 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5395 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5396 (int) floor ((double)new_note));
5398 show_verbose_cursor_text (buf);
5403 NoteDrag::finished (GdkEvent* ev, bool moved)
5406 /* no motion - select note */
5408 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5409 _editor->current_mouse_mode() == Editing::MouseDraw) {
5411 bool changed = false;
5413 if (_was_selected) {
5414 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5416 _region->note_deselected (_primary);
5420 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5421 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5423 if (!extend && !add && _region->selection_size() > 1) {
5424 _region->unique_select (_primary);
5426 } else if (extend) {
5427 _region->note_selected (_primary, true, true);
5430 /* it was added during button press */
5435 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5436 _editor->commit_reversible_selection_op();
5440 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5445 NoteDrag::aborted (bool)
5450 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5451 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5452 : Drag (editor, atv->base_item ())
5454 , _y_origin (atv->y_position())
5455 , _nothing_to_drag (false)
5457 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5458 setup (atv->lines ());
5461 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5462 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5463 : Drag (editor, rv->get_canvas_group ())
5465 , _y_origin (rv->get_time_axis_view().y_position())
5466 , _nothing_to_drag (false)
5469 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5471 list<boost::shared_ptr<AutomationLine> > lines;
5473 AudioRegionView* audio_view;
5474 AutomationRegionView* automation_view;
5475 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5476 lines.push_back (audio_view->get_gain_line ());
5477 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5478 lines.push_back (automation_view->line ());
5481 error << _("Automation range drag created for invalid region type") << endmsg;
5487 /** @param lines AutomationLines to drag.
5488 * @param offset Offset from the session start to the points in the AutomationLines.
5491 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5493 /* find the lines that overlap the ranges being dragged */
5494 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5495 while (i != lines.end ()) {
5496 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5499 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5501 /* check this range against all the AudioRanges that we are using */
5502 list<AudioRange>::const_iterator k = _ranges.begin ();
5503 while (k != _ranges.end()) {
5504 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5510 /* add it to our list if it overlaps at all */
5511 if (k != _ranges.end()) {
5516 _lines.push_back (n);
5522 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5526 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5528 return 1.0 - ((global_y - _y_origin) / line->height());
5532 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5534 const double v = list->eval(x);
5535 return _integral ? rint(v) : v;
5539 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5541 Drag::start_grab (event, cursor);
5543 /* Get line states before we start changing things */
5544 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5545 i->state = &i->line->get_state ();
5546 i->original_fraction = y_fraction (i->line, current_pointer_y());
5549 if (_ranges.empty()) {
5551 /* No selected time ranges: drag all points */
5552 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5553 uint32_t const N = i->line->npoints ();
5554 for (uint32_t j = 0; j < N; ++j) {
5555 i->points.push_back (i->line->nth (j));
5561 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5563 framecnt_t const half = (i->start + i->end) / 2;
5565 /* find the line that this audio range starts in */
5566 list<Line>::iterator j = _lines.begin();
5567 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5571 if (j != _lines.end()) {
5572 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5574 /* j is the line that this audio range starts in; fade into it;
5575 64 samples length plucked out of thin air.
5578 framepos_t a = i->start + 64;
5583 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5584 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5586 the_list->editor_add (p, value (the_list, p), false);
5587 the_list->editor_add (q, value (the_list, q), false);
5590 /* same thing for the end */
5593 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5597 if (j != _lines.end()) {
5598 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5600 /* j is the line that this audio range starts in; fade out of it;
5601 64 samples length plucked out of thin air.
5604 framepos_t b = i->end - 64;
5609 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5610 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5612 the_list->editor_add (p, value (the_list, p), false);
5613 the_list->editor_add (q, value (the_list, q), false);
5617 _nothing_to_drag = true;
5619 /* Find all the points that should be dragged and put them in the relevant
5620 points lists in the Line structs.
5623 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5625 uint32_t const N = i->line->npoints ();
5626 for (uint32_t j = 0; j < N; ++j) {
5628 /* here's a control point on this line */
5629 ControlPoint* p = i->line->nth (j);
5630 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5632 /* see if it's inside a range */
5633 list<AudioRange>::const_iterator k = _ranges.begin ();
5634 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5638 if (k != _ranges.end()) {
5639 /* dragging this point */
5640 _nothing_to_drag = false;
5641 i->points.push_back (p);
5647 if (_nothing_to_drag) {
5653 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5655 if (_nothing_to_drag) {
5660 _editor->begin_reversible_command (_("automation range move"));
5661 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5662 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5666 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5667 float const f = y_fraction (l->line, current_pointer_y());
5668 /* we are ignoring x position for this drag, so we can just pass in anything */
5670 l->line->drag_motion (0, f, true, false, ignored);
5671 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5676 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5678 if (_nothing_to_drag || !motion_occurred) {
5682 motion (event, false);
5683 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5684 i->line->end_drag (false, 0);
5687 _editor->commit_reversible_command ();
5691 AutomationRangeDrag::aborted (bool)
5693 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5698 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5700 , initial_time_axis_view (itav)
5702 /* note that time_axis_view may be null if the regionview was created
5703 * as part of a copy operation.
5705 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5706 layer = v->region()->layer ();
5707 initial_y = v->get_canvas_group()->position().y;
5708 initial_playlist = v->region()->playlist ();
5709 initial_position = v->region()->position ();
5710 initial_end = v->region()->position () + v->region()->length ();
5713 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5714 : Drag (e, i->canvas_item ())
5717 , _cumulative_dx (0)
5719 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5720 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5725 PatchChangeDrag::motion (GdkEvent* ev, bool)
5727 framepos_t f = adjusted_current_frame (ev);
5728 boost::shared_ptr<Region> r = _region_view->region ();
5729 f = max (f, r->position ());
5730 f = min (f, r->last_frame ());
5732 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5733 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5734 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5735 _cumulative_dx = dxu;
5739 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5741 if (!movement_occurred) {
5745 boost::shared_ptr<Region> r (_region_view->region ());
5746 framepos_t f = adjusted_current_frame (ev);
5747 f = max (f, r->position ());
5748 f = min (f, r->last_frame ());
5750 _region_view->move_patch_change (
5752 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5757 PatchChangeDrag::aborted (bool)
5759 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5763 PatchChangeDrag::setup_pointer_frame_offset ()
5765 boost::shared_ptr<Region> region = _region_view->region ();
5766 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5769 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5770 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5777 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5779 _region_view->update_drag_selection (
5781 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5785 MidiRubberbandSelectDrag::deselect_things ()
5790 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5791 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5794 _vertical_only = true;
5798 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5800 double const y = _region_view->midi_view()->y_position ();
5802 y1 = max (0.0, y1 - y);
5803 y2 = max (0.0, y2 - y);
5805 _region_view->update_vertical_drag_selection (
5808 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5813 MidiVerticalSelectDrag::deselect_things ()
5818 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5819 : RubberbandSelectDrag (e, i)
5825 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5827 if (drag_in_progress) {
5828 /* We just want to select things at the end of the drag, not during it */
5832 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5834 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5836 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5838 _editor->commit_reversible_selection_op ();
5842 EditorRubberbandSelectDrag::deselect_things ()
5844 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5846 _editor->selection->clear_tracks();
5847 _editor->selection->clear_regions();
5848 _editor->selection->clear_points ();
5849 _editor->selection->clear_lines ();
5850 _editor->selection->clear_midi_notes ();
5852 _editor->commit_reversible_selection_op();
5855 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5860 _note[0] = _note[1] = 0;
5863 NoteCreateDrag::~NoteCreateDrag ()
5869 NoteCreateDrag::grid_frames (framepos_t t) const
5872 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5874 grid_beats = Evoral::Beats(1);
5877 return _region_view->region_beats_to_region_frames (grid_beats);
5881 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5883 Drag::start_grab (event, cursor);
5885 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5887 framepos_t pf = _drags->current_pointer_frame ();
5888 framecnt_t const g = grid_frames (pf);
5890 /* Hack so that we always snap to the note that we are over, instead of snapping
5891 to the next one if we're more than halfway through the one we're over.
5893 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5897 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5898 _note[1] = _note[0];
5900 MidiStreamView* sv = _region_view->midi_stream_view ();
5901 double const x = _editor->sample_to_pixel (_note[0]);
5902 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5904 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5905 _drag_rect->set_outline_all ();
5906 _drag_rect->set_outline_color (0xffffff99);
5907 _drag_rect->set_fill_color (0xffffff66);
5911 NoteCreateDrag::motion (GdkEvent* event, bool)
5913 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5914 double const x0 = _editor->sample_to_pixel (_note[0]);
5915 double const x1 = _editor->sample_to_pixel (_note[1]);
5916 _drag_rect->set_x0 (std::min(x0, x1));
5917 _drag_rect->set_x1 (std::max(x0, x1));
5921 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5923 if (!had_movement) {
5927 framepos_t const start = min (_note[0], _note[1]);
5928 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5930 framecnt_t const g = grid_frames (start);
5931 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5933 if (_editor->snap_mode() == SnapNormal && length < g) {
5937 Evoral::Beats length_beats = max (
5938 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5940 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5944 NoteCreateDrag::y_to_region (double y) const
5947 _region_view->get_canvas_group()->canvas_to_item (x, y);
5952 NoteCreateDrag::aborted (bool)
5957 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5962 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5966 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5968 Drag::start_grab (event, cursor);
5972 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5978 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5981 distance = _drags->current_pointer_x() - grab_x();
5982 len = ar->fade_in()->back()->when;
5984 distance = grab_x() - _drags->current_pointer_x();
5985 len = ar->fade_out()->back()->when;
5988 /* how long should it be ? */
5990 new_length = len + _editor->pixel_to_sample (distance);
5992 /* now check with the region that this is legal */
5994 new_length = ar->verify_xfade_bounds (new_length, start);
5997 arv->reset_fade_in_shape_width (ar, new_length);
5999 arv->reset_fade_out_shape_width (ar, new_length);
6004 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6010 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6013 distance = _drags->current_pointer_x() - grab_x();
6014 len = ar->fade_in()->back()->when;
6016 distance = grab_x() - _drags->current_pointer_x();
6017 len = ar->fade_out()->back()->when;
6020 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6022 _editor->begin_reversible_command ("xfade trim");
6023 ar->playlist()->clear_owned_changes ();
6026 ar->set_fade_in_length (new_length);
6028 ar->set_fade_out_length (new_length);
6031 /* Adjusting the xfade may affect other regions in the playlist, so we need
6032 to get undo Commands from the whole playlist rather than just the
6036 vector<Command*> cmds;
6037 ar->playlist()->rdiff (cmds);
6038 _editor->session()->add_commands (cmds);
6039 _editor->commit_reversible_command ();
6044 CrossfadeEdgeDrag::aborted (bool)
6047 // arv->redraw_start_xfade ();
6049 // arv->redraw_end_xfade ();
6053 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6054 : Drag (e, item, true)
6055 , line (new EditorCursor (*e))
6057 line->set_position (pos);
6061 RegionCutDrag::~RegionCutDrag ()
6067 RegionCutDrag::motion (GdkEvent*, bool)
6069 framepos_t where = _drags->current_pointer_frame();
6070 _editor->snap_to (where);
6072 line->set_position (where);
6076 RegionCutDrag::finished (GdkEvent*, bool)
6078 _editor->get_track_canvas()->canvas()->re_enter();
6080 framepos_t pos = _drags->current_pointer_frame();
6084 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6090 _editor->split_regions_at (pos, rs);
6094 RegionCutDrag::aborted (bool)