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 "ui_config.h"
68 #include "verbose_cursor.h"
71 using namespace ARDOUR;
74 using namespace Gtkmm2ext;
75 using namespace Editing;
76 using namespace ArdourCanvas;
78 using Gtkmm2ext::Keyboard;
80 double ControlPointDrag::_zero_gain_fraction = -1.0;
82 DragManager::DragManager (Editor* e)
85 , _current_pointer_frame (0)
89 DragManager::~DragManager ()
94 /** Call abort for each active drag */
100 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
105 if (!_drags.empty ()) {
106 _editor->set_follow_playhead (_old_follow_playhead, false);
110 _editor->abort_reversible_command();
116 DragManager::add (Drag* d)
118 d->set_manager (this);
119 _drags.push_back (d);
123 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
125 d->set_manager (this);
126 _drags.push_back (d);
131 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
133 /* Prevent follow playhead during the drag to be nice to the user */
134 _old_follow_playhead = _editor->follow_playhead ();
135 _editor->set_follow_playhead (false);
137 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
139 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
140 (*i)->start_grab (e, c);
144 /** Call end_grab for each active drag.
145 * @return true if any drag reported movement having occurred.
148 DragManager::end_grab (GdkEvent* e)
153 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
154 bool const t = (*i)->end_grab (e);
165 _editor->set_follow_playhead (_old_follow_playhead, false);
171 DragManager::mark_double_click ()
173 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
174 (*i)->set_double_click (true);
179 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
183 /* calling this implies that we expect the event to have canvas
186 * Can we guarantee that this is true?
189 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
191 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
192 bool const t = (*i)->motion_handler (e, from_autoscroll);
193 /* run all handlers; return true if at least one of them
194 returns true (indicating that the event has been handled).
206 DragManager::have_item (ArdourCanvas::Item* i) const
208 list<Drag*>::const_iterator j = _drags.begin ();
209 while (j != _drags.end() && (*j)->item () != i) {
213 return j != _drags.end ();
216 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
219 , _pointer_frame_offset (0)
220 , _x_constrained (false)
221 , _y_constrained (false)
222 , _trackview_only (trackview_only)
223 , _move_threshold_passed (false)
224 , _starting_point_passed (false)
225 , _initially_vertical (false)
226 , _was_double_click (false)
227 , _raw_grab_frame (0)
229 , _last_pointer_frame (0)
236 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
242 _cursor_ctx = CursorContext::create (*_editor, cursor);
244 _cursor_ctx->change (cursor);
251 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
254 /* we set up x/y dragging constraints on first move */
256 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
258 setup_pointer_frame_offset ();
259 _grab_frame = adjusted_frame (_raw_grab_frame, event);
260 _last_pointer_frame = _grab_frame;
261 _last_pointer_x = _grab_x;
263 if (_trackview_only) {
264 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
267 _last_pointer_y = _grab_y;
271 if (!_editor->cursors()->is_invalid (cursor)) {
272 /* CAIROCANVAS need a variant here that passes *cursor */
273 _cursor_ctx = CursorContext::create (*_editor, cursor);
276 if (_editor->session() && _editor->session()->transport_rolling()) {
279 _was_rolling = false;
282 switch (_editor->snap_type()) {
283 case SnapToRegionStart:
284 case SnapToRegionEnd:
285 case SnapToRegionSync:
286 case SnapToRegionBoundary:
287 _editor->build_region_boundary_cache ();
294 /** Call to end a drag `successfully'. Ungrabs item and calls
295 * subclass' finished() method.
297 * @param event GDK event, or 0.
298 * @return true if some movement occurred, otherwise false.
301 Drag::end_grab (GdkEvent* event)
303 _editor->stop_canvas_autoscroll ();
307 finished (event, _move_threshold_passed);
309 _editor->verbose_cursor()->hide ();
312 return _move_threshold_passed;
316 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
320 if (f > _pointer_frame_offset) {
321 pos = f - _pointer_frame_offset;
325 _editor->snap_to_with_modifier (pos, event);
332 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
334 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
338 Drag::snap_delta (guint state) const
340 if (ArdourKeyboard::indicates_snap_delta (state)) {
348 Drag::current_pointer_x() const
350 return _drags->current_pointer_x ();
354 Drag::current_pointer_y () const
356 if (!_trackview_only) {
357 return _drags->current_pointer_y ();
360 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
364 Drag::setup_snap_delta (framepos_t pos)
366 framepos_t temp = pos;
367 _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
368 _snap_delta = temp - pos;
372 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
374 /* check to see if we have moved in any way that matters since the last motion event */
375 if (_move_threshold_passed &&
376 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
377 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
381 pair<framecnt_t, int> const threshold = move_threshold ();
383 bool const old_move_threshold_passed = _move_threshold_passed;
385 if (!_move_threshold_passed) {
387 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
388 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
390 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
393 if (active (_editor->mouse_mode) && _move_threshold_passed) {
395 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
397 if (old_move_threshold_passed != _move_threshold_passed) {
401 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
402 _initially_vertical = true;
404 _initially_vertical = false;
406 /** check constraints for this drag.
407 * Note that the current convention is to use "contains" for
408 * key modifiers during motion and "equals" when initiating a drag.
409 * In this case we haven't moved yet, so "equals" applies here.
411 if (Config->get_edit_mode() != Lock) {
412 if (event->motion.state & Gdk::BUTTON2_MASK) {
413 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
414 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
415 _x_constrained = false;
416 _y_constrained = true;
418 _x_constrained = true;
419 _y_constrained = false;
421 } else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
422 // if dragging normally, the motion is constrained to the first direction of movement.
423 if (_initially_vertical) {
424 _x_constrained = true;
425 _y_constrained = false;
427 _x_constrained = false;
428 _y_constrained = true;
432 if (event->button.state & Gdk::BUTTON2_MASK) {
433 _x_constrained = false;
435 _x_constrained = true;
437 _y_constrained = false;
441 if (!from_autoscroll) {
442 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
445 if (!_editor->autoscroll_active() || from_autoscroll) {
448 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
450 motion (event, first_move && !_starting_point_passed);
452 if (first_move && !_starting_point_passed) {
453 _starting_point_passed = true;
456 _last_pointer_x = _drags->current_pointer_x ();
457 _last_pointer_y = current_pointer_y ();
458 _last_pointer_frame = adjusted_current_frame (event);
468 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
476 aborted (_move_threshold_passed);
478 _editor->stop_canvas_autoscroll ();
479 _editor->verbose_cursor()->hide ();
483 Drag::show_verbose_cursor_time (framepos_t frame)
485 _editor->verbose_cursor()->set_time (frame);
486 _editor->verbose_cursor()->show ();
490 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
492 _editor->verbose_cursor()->set_duration (start, end);
493 _editor->verbose_cursor()->show ();
497 Drag::show_verbose_cursor_text (string const & text)
499 _editor->verbose_cursor()->set (text);
500 _editor->verbose_cursor()->show ();
503 boost::shared_ptr<Region>
504 Drag::add_midi_region (MidiTimeAxisView* view)
506 if (_editor->session()) {
507 const TempoMap& map (_editor->session()->tempo_map());
508 framecnt_t pos = grab_frame();
509 const Meter& m = map.meter_at (pos);
510 /* not that the frame rate used here can be affected by pull up/down which
513 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
514 return view->add_region (grab_frame(), len, true);
517 return boost::shared_ptr<Region>();
520 struct EditorOrderTimeAxisViewSorter {
521 bool operator() (TimeAxisView* a, TimeAxisView* b) {
522 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
523 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
525 return ra->route()->order_key () < rb->route()->order_key ();
529 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
534 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
536 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
537 as some of the regions we are dragging may be on such tracks.
540 TrackViewList track_views = _editor->track_views;
541 track_views.sort (EditorOrderTimeAxisViewSorter ());
543 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
544 _time_axis_views.push_back (*i);
546 TimeAxisView::Children children_list = (*i)->get_child_list ();
547 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
548 _time_axis_views.push_back (j->get());
552 /* the list of views can be empty at this point if this is a region list-insert drag
555 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
556 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
559 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
563 RegionDrag::region_going_away (RegionView* v)
565 list<DraggingView>::iterator i = _views.begin ();
566 while (i != _views.end() && i->view != v) {
570 if (i != _views.end()) {
575 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
576 * or -1 if it is not found.
579 RegionDrag::find_time_axis_view (TimeAxisView* t) const
582 int const N = _time_axis_views.size ();
583 while (i < N && _time_axis_views[i] != t) {
587 if (_time_axis_views[i] != t) {
594 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
595 : RegionDrag (e, i, p, v)
597 , _ignore_video_lock (false)
599 , _last_pointer_time_axis_view (0)
600 , _last_pointer_layer (0)
605 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
609 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
611 Drag::start_grab (event, cursor);
612 setup_snap_delta (_last_frame_position);
614 show_verbose_cursor_time (_last_frame_position);
616 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
618 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
619 assert(_last_pointer_time_axis_view >= 0);
620 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
623 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
624 _ignore_video_lock = true;
628 /* cross track dragging seems broken here. disabled for now. */
629 _y_constrained = true;
634 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
636 /* compute the amount of pointer motion in frames, and where
637 the region would be if we moved it by that much.
639 *pending_region_position = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
641 framepos_t sync_frame;
642 framecnt_t sync_offset;
645 sync_offset = _primary->region()->sync_offset (sync_dir);
647 /* we don't handle a sync point that lies before zero.
649 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
651 sync_frame = *pending_region_position + (sync_dir * sync_offset);
653 _editor->snap_to_with_modifier (sync_frame, event);
655 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - snap_delta (event->button.state);
658 *pending_region_position = _last_frame_position;
661 if (*pending_region_position > max_framepos - _primary->region()->length()) {
662 *pending_region_position = _last_frame_position;
667 bool const x_move_allowed = !_x_constrained;
669 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
671 /* x movement since last time (in pixels) */
672 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
674 /* total x movement */
675 framecnt_t total_dx = *pending_region_position;
676 if (regions_came_from_canvas()) {
677 total_dx = total_dx - grab_frame ();
680 /* check that no regions have gone off the start of the session */
681 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
682 if ((i->view->region()->position() + total_dx) < 0) {
684 *pending_region_position = _last_frame_position;
695 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
701 const int tavsize = _time_axis_views.size();
702 const int dt = delta > 0 ? +1 : -1;
704 int target = start + delta - skip;
706 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
707 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
709 while (current >= 0 && current != target) {
711 if (current < 0 && dt < 0) {
714 if (current >= tavsize && dt > 0) {
717 if (current < 0 || current >= tavsize) {
721 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
722 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
726 if (distance_only && current == start + delta) {
734 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
736 if (_y_constrained) {
740 const int tavsize = _time_axis_views.size();
741 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
742 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
743 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
745 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
746 /* already in the drop zone */
747 if (delta_track >= 0) {
748 /* downward motion - OK if others are still not in the dropzone */
757 } else if (n >= tavsize) {
758 /* downward motion into drop zone. That's fine. */
762 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
763 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
764 /* not a track, or the wrong type */
768 double const l = i->layer + delta_layer;
770 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
771 mode to allow the user to place a region below another on layer 0.
773 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
774 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
775 If it has, the layers will be munged later anyway, so it's ok.
781 /* all regions being dragged are ok with this change */
785 struct DraggingViewSorter {
786 bool operator() (const DraggingView& a, const DraggingView& b) {
787 return a.time_axis_view < b.time_axis_view;
792 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
794 double delta_layer = 0;
795 int delta_time_axis_view = 0;
796 int current_pointer_time_axis_view = -1;
798 assert (!_views.empty ());
800 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
802 /* Find the TimeAxisView that the pointer is now over */
803 const double cur_y = current_pointer_y ();
804 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
805 TimeAxisView* tv = r.first;
807 if (!tv && cur_y < 0) {
808 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
812 /* find drop-zone y-position */
813 Coord last_track_bottom_edge;
814 last_track_bottom_edge = 0;
815 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
816 if (!(*t)->hidden()) {
817 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
822 if (tv && tv->view()) {
823 /* the mouse is over a track */
824 double layer = r.second;
826 if (first_move && tv->view()->layer_display() == Stacked) {
827 tv->view()->set_layer_display (Expanded);
830 /* Here's the current pointer position in terms of time axis view and layer */
831 current_pointer_time_axis_view = find_time_axis_view (tv);
832 assert(current_pointer_time_axis_view >= 0);
834 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
836 /* Work out the change in y */
838 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
839 if (!rtv || !rtv->is_track()) {
840 /* ignore busses early on. we can't move any regions on them */
841 } else if (_last_pointer_time_axis_view < 0) {
842 /* Was in the drop-zone, now over a track.
843 * Hence it must be an upward move (from the bottom)
845 * track_index is still -1, so delta must be set to
846 * move up the correct number of tracks from the bottom.
848 * This is necessary because steps may be skipped if
849 * the bottom-most track is not a valid target and/or
850 * if there are hidden tracks at the bottom.
851 * Hence the initial offset (_ddropzone) as well as the
852 * last valid pointer position (_pdropzone) need to be
853 * taken into account.
855 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
857 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
860 /* TODO needs adjustment per DraggingView,
862 * e.g. select one region on the top-layer of a track
863 * and one region which is at the bottom-layer of another track
866 * Indicated drop-zones and layering is wrong.
867 * and may infer additional layers on the target-track
868 * (depending how many layers the original track had).
870 * Or select two regions (different layers) on a same track,
871 * move across a non-layer track.. -> layering info is lost.
872 * on drop either of the regions may be on top.
874 * Proposed solution: screw it :) well,
875 * don't use delta_layer, use an absolute value
876 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
877 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
878 * 3) iterate over all DraggingView, find the one that is over the track with most layers
879 * 4) proportionally scale layer to layers available on target
881 delta_layer = current_pointer_layer - _last_pointer_layer;
884 /* for automation lanes, there is a TimeAxisView but no ->view()
885 * if (!tv) -> dropzone
887 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
888 /* Moving into the drop-zone.. */
889 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
890 /* delta_time_axis_view may not be sufficient to move into the DZ
891 * the mouse may enter it, but it may not be a valid move due to
894 * -> remember the delta needed to move into the dropzone
896 _ddropzone = delta_time_axis_view;
897 /* ..but subtract hidden tracks (or routes) at the bottom.
898 * we silently move mover them
900 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
901 - _time_axis_views.size();
903 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
904 /* move around inside the zone.
905 * This allows to move further down until all regions are in the zone.
907 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
908 assert(ptr_y >= last_track_bottom_edge);
909 assert(_ddropzone > 0);
911 /* calculate mouse position in 'tracks' below last track. */
912 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
913 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
915 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
917 delta_time_axis_view = dzpos - _pdropzone;
918 } else if (dzpos < _pdropzone && _ndropzone > 0) {
919 // move up inside the DZ
920 delta_time_axis_view = dzpos - _pdropzone;
924 /* Work out the change in x */
925 framepos_t pending_region_position;
926 double const x_delta = compute_x_delta (event, &pending_region_position);
927 _last_frame_position = pending_region_position;
929 /* calculate hidden tracks in current y-axis delta */
931 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
932 /* The mouse is more than one track below the dropzone.
933 * distance calculation is not needed (and would not work, either
934 * because the dropzone is "packed").
936 * Except when [partially] moving regions out of dropzone in a large step.
937 * (the mouse may or may not remain in the DZ)
938 * Hidden tracks at the bottom of the TAV need to be skipped.
940 * This also handles the case if the mouse entered the DZ
941 * in a large step (exessive delta), either due to fast-movement,
942 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
944 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
945 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
947 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
948 -_time_axis_views.size() - dt;
951 else if (_last_pointer_time_axis_view < 0) {
952 /* Moving out of the zone. Check for hidden tracks at the bottom. */
953 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
954 -_time_axis_views.size() - delta_time_axis_view;
956 /* calculate hidden tracks that are skipped by the pointer movement */
957 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
958 - _last_pointer_time_axis_view
959 - delta_time_axis_view;
962 /* Verify change in y */
963 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
964 /* this y movement is not allowed, so do no y movement this time */
965 delta_time_axis_view = 0;
970 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
971 /* haven't reached next snap point, and we're not switching
972 trackviews nor layers. nothing to do.
977 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
978 PlaylistDropzoneMap playlist_dropzone_map;
979 _ndropzone = 0; // number of elements currently in the dropzone
982 /* sort views by time_axis.
983 * This retains track order in the dropzone, regardless
984 * of actual selection order
986 _views.sort (DraggingViewSorter());
988 /* count number of distinct tracks of all regions
989 * being dragged, used for dropzone.
992 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
993 if (i->time_axis_view != prev_track) {
994 prev_track = i->time_axis_view;
1000 _views.back().time_axis_view -
1001 _views.front().time_axis_view;
1003 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1004 - _views.back().time_axis_view;
1006 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1010 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1012 RegionView* rv = i->view;
1017 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1024 /* reparent the regionview into a group above all
1028 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1029 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1030 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1031 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1032 /* move the item so that it continues to appear at the
1033 same location now that its parent has changed.
1035 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1038 /* If we have moved tracks, we'll fudge the layer delta so that the
1039 region gets moved back onto layer 0 on its new track; this avoids
1040 confusion when dragging regions from non-zero layers onto different
1043 double this_delta_layer = delta_layer;
1044 if (delta_time_axis_view != 0) {
1045 this_delta_layer = - i->layer;
1048 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1050 int track_index = i->time_axis_view + this_delta_time_axis_view;
1051 assert(track_index >= 0);
1053 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1054 /* Track is in the Dropzone */
1056 i->time_axis_view = track_index;
1057 assert(i->time_axis_view >= (int) _time_axis_views.size());
1060 double yposition = 0;
1061 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1062 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1065 /* store index of each new playlist as a negative count, starting at -1 */
1067 if (pdz == playlist_dropzone_map.end()) {
1068 /* compute where this new track (which doesn't exist yet) will live
1071 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1073 /* How high is this region view ? */
1075 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1076 ArdourCanvas::Rect bbox;
1079 bbox = obbox.get ();
1082 last_track_bottom_edge += bbox.height();
1084 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1087 yposition = pdz->second;
1090 /* values are zero or negative, hence the use of min() */
1091 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1096 /* The TimeAxisView that this region is now over */
1097 TimeAxisView* current_tv = _time_axis_views[track_index];
1099 /* Ensure it is moved from stacked -> expanded if appropriate */
1100 if (current_tv->view()->layer_display() == Stacked) {
1101 current_tv->view()->set_layer_display (Expanded);
1104 /* We're only allowed to go -ve in layer on Expanded views */
1105 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1106 this_delta_layer = - i->layer;
1110 rv->set_height (current_tv->view()->child_height ());
1112 /* Update show/hidden status as the region view may have come from a hidden track,
1113 or have moved to one.
1115 if (current_tv->hidden ()) {
1116 rv->get_canvas_group()->hide ();
1118 rv->get_canvas_group()->show ();
1121 /* Update the DraggingView */
1122 i->time_axis_view = track_index;
1123 i->layer += this_delta_layer;
1126 _editor->mouse_brush_insert_region (rv, pending_region_position);
1130 /* Get the y coordinate of the top of the track that this region is now over */
1131 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1133 /* And adjust for the layer that it should be on */
1134 StreamView* cv = current_tv->view ();
1135 switch (cv->layer_display ()) {
1139 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1142 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1146 /* need to get the parent of the regionview
1147 * canvas group and get its position in
1148 * equivalent coordinate space as the trackview
1149 * we are now dragging over.
1152 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1157 /* Now move the region view */
1158 rv->move (x_delta, y_delta);
1160 } /* foreach region */
1162 _total_x_delta += x_delta;
1164 if (x_delta != 0 && !_brushing) {
1165 show_verbose_cursor_time (_last_frame_position);
1168 /* keep track of pointer movement */
1170 /* the pointer is currently over a time axis view */
1172 if (_last_pointer_time_axis_view < 0) {
1173 /* last motion event was not over a time axis view
1174 * or last y-movement out of the dropzone was not valid
1177 if (delta_time_axis_view < 0) {
1178 /* in the drop zone, moving up */
1180 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1181 * We do not use negative _last_pointer_time_axis_view because
1182 * the dropzone is "packed" (the actual track offset is ignored)
1184 * As opposed to the actual number
1185 * of elements in the dropzone (_ndropzone)
1186 * _pdropzone is not constrained. This is necessary
1187 * to allow moving multiple regions with y-distance
1190 * There can be 0 elements in the dropzone,
1191 * even though the drag-pointer is inside the DZ.
1194 * [ Audio-track, Midi-track, Audio-track, DZ ]
1195 * move regions from both audio tracks at the same time into the
1196 * DZ by grabbing the region in the bottom track.
1198 assert(current_pointer_time_axis_view >= 0);
1199 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1203 /* only move out of the zone if the movement is OK */
1204 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1205 assert(delta_time_axis_view < 0);
1206 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1207 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1208 * the current position can be calculated as follows:
1210 // a well placed oofus attack can still throw this off.
1211 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1212 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1215 /* last motion event was also over a time axis view */
1216 _last_pointer_time_axis_view += delta_time_axis_view;
1217 assert(_last_pointer_time_axis_view >= 0);
1222 /* the pointer is not over a time axis view */
1223 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1224 _pdropzone += delta_time_axis_view - delta_skip;
1225 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1228 _last_pointer_layer += delta_layer;
1232 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1234 if (_copy && first_move) {
1235 if (_x_constrained && !_brushing) {
1236 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1237 } else if (!_brushing) {
1238 _editor->begin_reversible_command (Operations::region_copy);
1239 } else if (_brushing) {
1240 _editor->begin_reversible_command (Operations::drag_region_brush);
1242 /* duplicate the regionview(s) and region(s) */
1244 list<DraggingView> new_regionviews;
1246 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1248 RegionView* rv = i->view;
1249 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1250 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1252 const boost::shared_ptr<const Region> original = rv->region();
1253 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1254 region_copy->set_position (original->position());
1255 /* need to set this so that the drop zone code can work. This doesn't
1256 actually put the region into the playlist, but just sets a weak pointer
1259 region_copy->set_playlist (original->playlist());
1263 boost::shared_ptr<AudioRegion> audioregion_copy
1264 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1266 nrv = new AudioRegionView (*arv, audioregion_copy);
1268 boost::shared_ptr<MidiRegion> midiregion_copy
1269 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1270 nrv = new MidiRegionView (*mrv, midiregion_copy);
1275 nrv->get_canvas_group()->show ();
1276 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1278 /* swap _primary to the copy */
1280 if (rv == _primary) {
1284 /* ..and deselect the one we copied */
1286 rv->set_selected (false);
1289 if (!new_regionviews.empty()) {
1291 /* reflect the fact that we are dragging the copies */
1293 _views = new_regionviews;
1295 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1298 } else if (!_copy && first_move) {
1299 if (_x_constrained && !_brushing) {
1300 _editor->begin_reversible_command (_("fixed time region drag"));
1301 } else if (!_brushing) {
1302 _editor->begin_reversible_command (Operations::region_drag);
1303 } else if (_brushing) {
1304 _editor->begin_reversible_command (Operations::drag_region_brush);
1307 RegionMotionDrag::motion (event, first_move);
1311 RegionMotionDrag::finished (GdkEvent *, bool)
1313 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1314 if (!(*i)->view()) {
1318 if ((*i)->view()->layer_display() == Expanded) {
1319 (*i)->view()->set_layer_display (Stacked);
1325 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1327 RegionMotionDrag::finished (ev, movement_occurred);
1329 if (!movement_occurred) {
1333 if (was_double_click() && !_views.empty()) {
1334 DraggingView dv = _views.front();
1335 dv.view->show_region_editor ();
1342 assert (!_views.empty ());
1344 /* We might have hidden region views so that they weren't visible during the drag
1345 (when they have been reparented). Now everything can be shown again, as region
1346 views are back in their track parent groups.
1348 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1349 i->view->get_canvas_group()->show ();
1352 bool const changed_position = (_last_frame_position != _primary->region()->position());
1353 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1354 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1374 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1378 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1380 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1385 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1386 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1387 uint32_t output_chan = region->n_channels();
1388 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1389 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1391 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name());
1392 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1394 rtav->set_height (original->current_height());
1398 ChanCount one_midi_port (DataType::MIDI, 1);
1399 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1400 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1401 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1403 rtav->set_height (original->current_height());
1408 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1414 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1416 RegionSelection new_views;
1417 PlaylistSet modified_playlists;
1418 RouteTimeAxisView* new_time_axis_view = 0;
1421 /* all changes were made during motion event handlers */
1423 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1427 _editor->commit_reversible_command ();
1431 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1432 PlaylistMapping playlist_mapping;
1434 /* insert the regions into their new playlists */
1435 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1437 RouteTimeAxisView* dest_rtv = 0;
1439 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1445 if (changed_position && !_x_constrained) {
1446 where = i->view->region()->position() - drag_delta;
1448 where = i->view->region()->position();
1451 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1452 /* dragged to drop zone */
1454 PlaylistMapping::iterator pm;
1456 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1457 /* first region from this original playlist: create a new track */
1458 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1459 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1460 dest_rtv = new_time_axis_view;
1462 /* we already created a new track for regions from this playlist, use it */
1463 dest_rtv = pm->second;
1466 /* destination time axis view is the one we dragged to */
1467 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1470 if (dest_rtv != 0) {
1471 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1472 if (new_view != 0) {
1473 new_views.push_back (new_view);
1477 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1478 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1481 list<DraggingView>::const_iterator next = i;
1487 /* If we've created new regions either by copying or moving
1488 to a new track, we want to replace the old selection with the new ones
1491 if (new_views.size() > 0) {
1492 _editor->selection->set (new_views);
1495 /* write commands for the accumulated diffs for all our modified playlists */
1496 add_stateful_diff_commands_for_playlists (modified_playlists);
1498 _editor->commit_reversible_command ();
1502 RegionMoveDrag::finished_no_copy (
1503 bool const changed_position,
1504 bool const changed_tracks,
1505 framecnt_t const drag_delta
1508 RegionSelection new_views;
1509 PlaylistSet modified_playlists;
1510 PlaylistSet frozen_playlists;
1511 set<RouteTimeAxisView*> views_to_update;
1512 RouteTimeAxisView* new_time_axis_view = 0;
1514 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1515 PlaylistMapping playlist_mapping;
1517 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1519 RegionView* rv = i->view;
1520 RouteTimeAxisView* dest_rtv = 0;
1522 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1527 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1528 /* dragged to drop zone */
1530 PlaylistMapping::iterator pm;
1532 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1533 /* first region from this original playlist: create a new track */
1534 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1535 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1536 dest_rtv = new_time_axis_view;
1538 /* we already created a new track for regions from this playlist, use it */
1539 dest_rtv = pm->second;
1543 /* destination time axis view is the one we dragged to */
1544 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1549 double const dest_layer = i->layer;
1551 views_to_update.insert (dest_rtv);
1555 if (changed_position && !_x_constrained) {
1556 where = rv->region()->position() - drag_delta;
1558 where = rv->region()->position();
1561 if (changed_tracks) {
1563 /* insert into new playlist */
1565 RegionView* new_view = insert_region_into_playlist (
1566 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1569 if (new_view == 0) {
1574 new_views.push_back (new_view);
1576 /* remove from old playlist */
1578 /* the region that used to be in the old playlist is not
1579 moved to the new one - we use a copy of it. as a result,
1580 any existing editor for the region should no longer be
1583 rv->hide_region_editor();
1586 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1590 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1592 /* this movement may result in a crossfade being modified, or a layering change,
1593 so we need to get undo data from the playlist as well as the region.
1596 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1598 playlist->clear_changes ();
1601 rv->region()->clear_changes ();
1604 motion on the same track. plonk the previously reparented region
1605 back to its original canvas group (its streamview).
1606 No need to do anything for copies as they are fake regions which will be deleted.
1609 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1610 rv->get_canvas_group()->set_y_position (i->initial_y);
1613 /* just change the model */
1614 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1615 playlist->set_layer (rv->region(), dest_layer);
1618 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1620 r = frozen_playlists.insert (playlist);
1623 playlist->freeze ();
1626 rv->region()->set_position (where);
1627 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1630 if (changed_tracks) {
1632 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1633 was selected in all of them, then removing it from a playlist will have removed all
1634 trace of it from _views (i.e. there were N regions selected, we removed 1,
1635 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1636 corresponding regionview, and _views is now empty).
1638 This could have invalidated any and all iterators into _views.
1640 The heuristic we use here is: if the region selection is empty, break out of the loop
1641 here. if the region selection is not empty, then restart the loop because we know that
1642 we must have removed at least the region(view) we've just been working on as well as any
1643 that we processed on previous iterations.
1645 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1646 we can just iterate.
1650 if (_views.empty()) {
1661 /* If we've created new regions either by copying or moving
1662 to a new track, we want to replace the old selection with the new ones
1665 if (new_views.size() > 0) {
1666 _editor->selection->set (new_views);
1669 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1673 /* write commands for the accumulated diffs for all our modified playlists */
1674 add_stateful_diff_commands_for_playlists (modified_playlists);
1675 /* applies to _brushing */
1676 _editor->commit_reversible_command ();
1678 /* We have futzed with the layering of canvas items on our streamviews.
1679 If any region changed layer, this will have resulted in the stream
1680 views being asked to set up their region views, and all will be well.
1681 If not, we might now have badly-ordered region views. Ask the StreamViews
1682 involved to sort themselves out, just in case.
1685 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1686 (*i)->view()->playlist_layered ((*i)->track ());
1690 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1691 * @param region Region to remove.
1692 * @param playlist playlist To remove from.
1693 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1694 * that clear_changes () is only called once per playlist.
1697 RegionMoveDrag::remove_region_from_playlist (
1698 boost::shared_ptr<Region> region,
1699 boost::shared_ptr<Playlist> playlist,
1700 PlaylistSet& modified_playlists
1703 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1706 playlist->clear_changes ();
1709 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1713 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1714 * clearing the playlist's diff history first if necessary.
1715 * @param region Region to insert.
1716 * @param dest_rtv Destination RouteTimeAxisView.
1717 * @param dest_layer Destination layer.
1718 * @param where Destination position.
1719 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1720 * that clear_changes () is only called once per playlist.
1721 * @return New RegionView, or 0 if no insert was performed.
1724 RegionMoveDrag::insert_region_into_playlist (
1725 boost::shared_ptr<Region> region,
1726 RouteTimeAxisView* dest_rtv,
1729 PlaylistSet& modified_playlists
1732 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1733 if (!dest_playlist) {
1737 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1738 _new_region_view = 0;
1739 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1741 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1742 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1744 dest_playlist->clear_changes ();
1747 dest_playlist->add_region (region, where);
1749 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1750 dest_playlist->set_layer (region, dest_layer);
1755 assert (_new_region_view);
1757 return _new_region_view;
1761 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1763 _new_region_view = rv;
1767 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1769 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1770 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1772 _editor->session()->add_command (c);
1781 RegionMoveDrag::aborted (bool movement_occurred)
1785 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1786 list<DraggingView>::const_iterator next = i;
1795 RegionMotionDrag::aborted (movement_occurred);
1800 RegionMotionDrag::aborted (bool)
1802 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1804 StreamView* sview = (*i)->view();
1807 if (sview->layer_display() == Expanded) {
1808 sview->set_layer_display (Stacked);
1813 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1814 RegionView* rv = i->view;
1815 TimeAxisView* tv = &(rv->get_time_axis_view ());
1816 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1818 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1819 rv->get_canvas_group()->set_y_position (0);
1821 rv->move (-_total_x_delta, 0);
1822 rv->set_height (rtv->view()->child_height ());
1826 /** @param b true to brush, otherwise false.
1827 * @param c true to make copies of the regions being moved, otherwise false.
1829 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1830 : RegionMotionDrag (e, i, p, v, b)
1833 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1836 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1837 if (rtv && rtv->is_track()) {
1838 speed = rtv->track()->speed ();
1841 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1845 RegionMoveDrag::setup_pointer_frame_offset ()
1847 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1850 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1851 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1853 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1855 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1856 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1858 _primary = v->view()->create_region_view (r, false, false);
1860 _primary->get_canvas_group()->show ();
1861 _primary->set_position (pos, 0);
1862 _views.push_back (DraggingView (_primary, this, v));
1864 _last_frame_position = pos;
1866 _item = _primary->get_canvas_group ();
1870 RegionInsertDrag::finished (GdkEvent *, bool)
1872 int pos = _views.front().time_axis_view;
1873 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1875 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1877 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1878 _primary->get_canvas_group()->set_y_position (0);
1880 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1882 _editor->begin_reversible_command (Operations::insert_region);
1883 playlist->clear_changes ();
1884 playlist->add_region (_primary->region (), _last_frame_position);
1886 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1887 if (Config->get_edit_mode() == Ripple) {
1888 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1891 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1892 _editor->commit_reversible_command ();
1900 RegionInsertDrag::aborted (bool)
1907 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1908 : RegionMoveDrag (e, i, p, v, false, false)
1910 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1913 struct RegionSelectionByPosition {
1914 bool operator() (RegionView*a, RegionView* b) {
1915 return a->region()->position () < b->region()->position();
1920 RegionSpliceDrag::motion (GdkEvent* event, bool)
1922 /* Which trackview is this ? */
1924 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1925 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1927 /* The region motion is only processed if the pointer is over
1931 if (!tv || !tv->is_track()) {
1932 /* To make sure we hide the verbose canvas cursor when the mouse is
1933 not held over an audio track.
1935 _editor->verbose_cursor()->hide ();
1938 _editor->verbose_cursor()->show ();
1943 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1949 RegionSelection copy;
1950 _editor->selection->regions.by_position(copy);
1952 framepos_t const pf = adjusted_current_frame (event);
1954 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1956 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1962 boost::shared_ptr<Playlist> playlist;
1964 if ((playlist = atv->playlist()) == 0) {
1968 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1973 if (pf < (*i)->region()->last_frame() + 1) {
1977 if (pf > (*i)->region()->first_frame()) {
1983 playlist->shuffle ((*i)->region(), dir);
1988 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1990 RegionMoveDrag::finished (event, movement_occurred);
1994 RegionSpliceDrag::aborted (bool)
2004 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2007 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2009 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2010 RegionSelection to_ripple;
2011 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2012 if ((*i)->position() >= where) {
2013 to_ripple.push_back (rtv->view()->find_view(*i));
2017 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2018 if (!exclude.contains (*i)) {
2019 // the selection has already been added to _views
2021 if (drag_in_progress) {
2022 // do the same things that RegionMotionDrag::motion does when
2023 // first_move is true, for the region views that we're adding
2024 // to _views this time
2027 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2028 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2029 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2030 rvg->reparent (_editor->_drag_motion_group);
2032 // we only need to move in the y direction
2033 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2038 _views.push_back (DraggingView (*i, this, tav));
2044 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2047 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2048 // we added all the regions after the selection
2050 std::list<DraggingView>::iterator to_erase = i++;
2051 if (!_editor->selection->regions.contains (to_erase->view)) {
2052 // restore the non-selected regions to their original playlist & positions,
2053 // and then ripple them back by the length of the regions that were dragged away
2054 // do the same things as RegionMotionDrag::aborted
2056 RegionView *rv = to_erase->view;
2057 TimeAxisView* tv = &(rv->get_time_axis_view ());
2058 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2061 // plonk them back onto their own track
2062 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2063 rv->get_canvas_group()->set_y_position (0);
2067 // move the underlying region to match the view
2068 rv->region()->set_position (rv->region()->position() + amount);
2070 // restore the view to match the underlying region's original position
2071 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2074 rv->set_height (rtv->view()->child_height ());
2075 _views.erase (to_erase);
2081 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2083 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2085 return allow_moves_across_tracks;
2093 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2094 : RegionMoveDrag (e, i, p, v, false, false)
2096 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2097 // compute length of selection
2098 RegionSelection selected_regions = _editor->selection->regions;
2099 selection_length = selected_regions.end_frame() - selected_regions.start();
2101 // we'll only allow dragging to another track in ripple mode if all the regions
2102 // being dragged start off on the same track
2103 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2106 exclude = new RegionList;
2107 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2108 exclude->push_back((*i)->region());
2111 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2112 RegionSelection copy;
2113 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2115 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2116 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2118 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2119 // find ripple start point on each applicable playlist
2120 RegionView *first_selected_on_this_track = NULL;
2121 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2122 if ((*i)->region()->playlist() == (*pi)) {
2123 // region is on this playlist - it's the first, because they're sorted
2124 first_selected_on_this_track = *i;
2128 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2129 add_all_after_to_views (
2130 &first_selected_on_this_track->get_time_axis_view(),
2131 first_selected_on_this_track->region()->position(),
2132 selected_regions, false);
2135 if (allow_moves_across_tracks) {
2136 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2144 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2146 /* Which trackview is this ? */
2148 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2149 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2151 /* The region motion is only processed if the pointer is over
2155 if (!tv || !tv->is_track()) {
2156 /* To make sure we hide the verbose canvas cursor when the mouse is
2157 not held over an audiotrack.
2159 _editor->verbose_cursor()->hide ();
2163 framepos_t where = adjusted_current_frame (event);
2164 assert (where >= 0);
2166 double delta = compute_x_delta (event, &after);
2168 framecnt_t amount = _editor->pixel_to_sample (delta);
2170 if (allow_moves_across_tracks) {
2171 // all the originally selected regions were on the same track
2173 framecnt_t adjust = 0;
2174 if (prev_tav && tv != prev_tav) {
2175 // dragged onto a different track
2176 // remove the unselected regions from _views, restore them to their original positions
2177 // and add the regions after the drop point on the new playlist to _views instead.
2178 // undo the effect of rippling the previous playlist, and include the effect of removing
2179 // the dragged region(s) from this track
2181 remove_unselected_from_views (prev_amount, false);
2182 // ripple previous playlist according to the regions that have been removed onto the new playlist
2183 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2186 // move just the selected regions
2187 RegionMoveDrag::motion(event, first_move);
2189 // ensure that the ripple operation on the new playlist inserts selection_length time
2190 adjust = selection_length;
2191 // ripple the new current playlist
2192 tv->playlist()->ripple (where, amount+adjust, exclude);
2194 // add regions after point where drag entered this track to subsequent ripples
2195 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2198 // motion on same track
2199 RegionMoveDrag::motion(event, first_move);
2203 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2204 prev_position = where;
2206 // selection encompasses multiple tracks - just drag
2207 // cross-track drags are forbidden
2208 RegionMoveDrag::motion(event, first_move);
2211 if (!_x_constrained) {
2212 prev_amount += amount;
2215 _last_frame_position = after;
2219 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2221 if (!movement_occurred) {
2225 if (was_double_click() && !_views.empty()) {
2226 DraggingView dv = _views.front();
2227 dv.view->show_region_editor ();
2234 _editor->begin_reversible_command(_("Ripple drag"));
2236 // remove the regions being rippled from the dragging view, updating them to
2237 // their new positions
2238 remove_unselected_from_views (prev_amount, true);
2240 if (allow_moves_across_tracks) {
2242 // if regions were dragged across tracks, we've rippled any later
2243 // regions on the track the regions were dragged off, so we need
2244 // to add the original track to the undo record
2245 orig_tav->playlist()->clear_changes();
2246 vector<Command*> cmds;
2247 orig_tav->playlist()->rdiff (cmds);
2248 _editor->session()->add_commands (cmds);
2250 if (prev_tav && prev_tav != orig_tav) {
2251 prev_tav->playlist()->clear_changes();
2252 vector<Command*> cmds;
2253 prev_tav->playlist()->rdiff (cmds);
2254 _editor->session()->add_commands (cmds);
2257 // selection spanned multiple tracks - all will need adding to undo record
2259 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2260 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2262 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2263 (*pi)->clear_changes();
2264 vector<Command*> cmds;
2265 (*pi)->rdiff (cmds);
2266 _editor->session()->add_commands (cmds);
2270 // other modified playlists are added to undo by RegionMoveDrag::finished()
2271 RegionMoveDrag::finished (event, movement_occurred);
2272 _editor->commit_reversible_command();
2276 RegionRippleDrag::aborted (bool movement_occurred)
2278 RegionMoveDrag::aborted (movement_occurred);
2283 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2285 _view (dynamic_cast<MidiTimeAxisView*> (v))
2287 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2293 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2296 _region = add_midi_region (_view);
2297 _view->playlist()->freeze ();
2300 framepos_t const f = adjusted_current_frame (event);
2301 if (f < grab_frame()) {
2302 _region->set_position (f);
2305 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2306 so that if this region is duplicated, its duplicate starts on
2307 a snap point rather than 1 frame after a snap point. Otherwise things get
2308 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2309 place snapped notes at the start of the region.
2312 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2313 _region->set_length (len < 1 ? 1 : len);
2319 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2321 if (!movement_occurred) {
2322 add_midi_region (_view);
2324 _view->playlist()->thaw ();
2329 RegionCreateDrag::aborted (bool)
2332 _view->playlist()->thaw ();
2338 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2345 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2349 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2351 Gdk::Cursor* cursor;
2352 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2354 float x_fraction = cnote->mouse_x_fraction ();
2356 if (x_fraction > 0.0 && x_fraction < 0.25) {
2357 cursor = _editor->cursors()->left_side_trim;
2360 cursor = _editor->cursors()->right_side_trim;
2364 Drag::start_grab (event, cursor);
2366 region = &cnote->region_view();
2369 temp = region->snap_to_pixel (cnote->x0 (), true);
2370 _snap_delta = temp - cnote->x0 ();
2374 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2379 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2380 if (ms.size() > 1) {
2381 /* has to be relative, may make no sense otherwise */
2384 /* select this note; if it is already selected, preserve the existing selection,
2385 otherwise make this note the only one selected.
2387 region->note_selected (cnote, cnote->selected ());
2391 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2393 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2395 _editor->begin_reversible_command (_("resize notes"));
2397 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2398 MidiRegionSelection::iterator next;
2401 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2403 mrv->begin_resizing (at_front);
2409 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2410 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2412 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2416 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2418 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2419 if (_editor->snap_mode () != SnapOff) {
2423 if (_editor->snap_mode () == SnapOff) {
2425 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2426 if (apply_snap_delta) {
2432 if (apply_snap_delta) {
2436 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2442 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2444 if (!movement_occurred) {
2448 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2449 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2450 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2452 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2455 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2457 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2458 if (_editor->snap_mode () != SnapOff) {
2462 if (_editor->snap_mode () == SnapOff) {
2464 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2465 if (apply_snap_delta) {
2471 if (apply_snap_delta) {
2475 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2479 _editor->commit_reversible_command ();
2483 NoteResizeDrag::aborted (bool)
2485 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2486 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2487 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2489 mrv->abort_resizing ();
2494 AVDraggingView::AVDraggingView (RegionView* v)
2497 initial_position = v->region()->position ();
2500 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2503 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2506 TrackViewList empty;
2508 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2509 std::list<RegionView*> views = rs.by_layer();
2512 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2513 RegionView* rv = (*i);
2514 if (!rv->region()->video_locked()) {
2517 if (rv->region()->locked()) {
2520 _views.push_back (AVDraggingView (rv));
2525 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2527 Drag::start_grab (event);
2528 if (_editor->session() == 0) {
2532 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2538 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2542 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2543 _max_backwards_drag = (
2544 ARDOUR_UI::instance()->video_timeline->get_duration()
2545 + ARDOUR_UI::instance()->video_timeline->get_offset()
2546 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2549 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2550 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2551 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2554 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2557 Timecode::Time timecode;
2558 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2559 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);
2560 show_verbose_cursor_text (buf);
2564 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2566 if (_editor->session() == 0) {
2569 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2573 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2577 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2578 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2580 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2581 dt = - _max_backwards_drag;
2584 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2585 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2587 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2588 RegionView* rv = i->view;
2589 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2592 rv->region()->clear_changes ();
2593 rv->region()->suspend_property_changes();
2595 rv->region()->set_position(i->initial_position + dt);
2596 rv->region_changed(ARDOUR::Properties::position);
2599 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2600 Timecode::Time timecode;
2601 Timecode::Time timediff;
2603 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2604 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2605 snprintf (buf, sizeof (buf),
2606 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2607 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2608 , _("Video Start:"),
2609 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2611 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2613 show_verbose_cursor_text (buf);
2617 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2619 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2626 if (!movement_occurred || ! _editor->session()) {
2630 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2632 _editor->begin_reversible_command (_("Move Video"));
2634 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2635 ARDOUR_UI::instance()->video_timeline->save_undo();
2636 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2637 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2639 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2640 i->view->drag_end();
2641 i->view->region()->resume_property_changes ();
2643 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2646 _editor->session()->maybe_update_session_range(
2647 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2648 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2652 _editor->commit_reversible_command ();
2656 VideoTimeLineDrag::aborted (bool)
2658 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2661 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2662 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2664 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2665 i->view->region()->resume_property_changes ();
2666 i->view->region()->set_position(i->initial_position);
2670 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2671 : RegionDrag (e, i, p, v)
2672 , _preserve_fade_anchor (preserve_fade_anchor)
2673 , _jump_position_when_done (false)
2675 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2679 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2682 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2683 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2685 if (tv && tv->is_track()) {
2686 speed = tv->track()->speed();
2689 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2690 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2691 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2693 framepos_t const pf = adjusted_current_frame (event);
2694 setup_snap_delta (region_start);
2696 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2697 /* Move the contents of the region around without changing the region bounds */
2698 _operation = ContentsTrim;
2699 Drag::start_grab (event, _editor->cursors()->trimmer);
2701 /* These will get overridden for a point trim.*/
2702 if (pf < (region_start + region_length/2)) {
2703 /* closer to front */
2704 _operation = StartTrim;
2705 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2706 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2708 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2712 _operation = EndTrim;
2713 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2714 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2716 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2720 /* jump trim disabled for now
2721 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2722 _jump_position_when_done = true;
2726 switch (_operation) {
2728 show_verbose_cursor_time (region_start);
2729 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2730 i->view->trim_front_starting ();
2734 show_verbose_cursor_duration (region_start, region_end);
2737 show_verbose_cursor_time (pf);
2741 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2742 i->view->region()->suspend_property_changes ();
2747 TrimDrag::motion (GdkEvent* event, bool first_move)
2749 RegionView* rv = _primary;
2752 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2753 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2754 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2755 frameoffset_t frame_delta = 0;
2757 if (tv && tv->is_track()) {
2758 speed = tv->track()->speed();
2760 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2761 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2767 switch (_operation) {
2769 trim_type = "Region start trim";
2772 trim_type = "Region end trim";
2775 trim_type = "Region content trim";
2782 _editor->begin_reversible_command (trim_type);
2784 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2785 RegionView* rv = i->view;
2786 rv->enable_display (false);
2787 rv->region()->playlist()->clear_owned_changes ();
2789 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2792 arv->temporarily_hide_envelope ();
2796 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2797 insert_result = _editor->motion_frozen_playlists.insert (pl);
2799 if (insert_result.second) {
2805 bool non_overlap_trim = false;
2807 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2808 non_overlap_trim = true;
2811 /* contstrain trim to fade length */
2812 if (_preserve_fade_anchor) {
2813 switch (_operation) {
2815 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2816 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2818 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2819 if (ar->locked()) continue;
2820 framecnt_t len = ar->fade_in()->back()->when;
2821 if (len < dt) dt = min(dt, len);
2825 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2826 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2828 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2829 if (ar->locked()) continue;
2830 framecnt_t len = ar->fade_out()->back()->when;
2831 if (len < -dt) dt = max(dt, -len);
2840 switch (_operation) {
2842 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2843 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2844 if (changed && _preserve_fade_anchor) {
2845 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2847 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2848 framecnt_t len = ar->fade_in()->back()->when;
2849 framecnt_t diff = ar->first_frame() - i->initial_position;
2850 framepos_t new_length = len - diff;
2851 i->anchored_fade_length = min (ar->length(), new_length);
2852 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2853 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2860 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2861 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2862 if (changed && _preserve_fade_anchor) {
2863 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2865 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2866 framecnt_t len = ar->fade_out()->back()->when;
2867 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2868 framepos_t new_length = len + diff;
2869 i->anchored_fade_length = min (ar->length(), new_length);
2870 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2871 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2879 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2881 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2882 i->view->move_contents (frame_delta);
2888 switch (_operation) {
2890 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2893 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2896 // show_verbose_cursor_time (frame_delta);
2902 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2904 if (movement_occurred) {
2905 motion (event, false);
2907 if (_operation == StartTrim) {
2908 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2910 /* This must happen before the region's StatefulDiffCommand is created, as it may
2911 `correct' (ahem) the region's _start from being negative to being zero. It
2912 needs to be zero in the undo record.
2914 i->view->trim_front_ending ();
2916 if (_preserve_fade_anchor) {
2917 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2919 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2920 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2921 ar->set_fade_in_length(i->anchored_fade_length);
2922 ar->set_fade_in_active(true);
2925 if (_jump_position_when_done) {
2926 i->view->region()->set_position (i->initial_position);
2929 } else if (_operation == EndTrim) {
2930 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2931 if (_preserve_fade_anchor) {
2932 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2934 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2935 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2936 ar->set_fade_out_length(i->anchored_fade_length);
2937 ar->set_fade_out_active(true);
2940 if (_jump_position_when_done) {
2941 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2946 if (!_views.empty()) {
2947 if (_operation == StartTrim) {
2948 _editor->maybe_locate_with_edit_preroll(
2949 _views.begin()->view->region()->position());
2951 if (_operation == EndTrim) {
2952 _editor->maybe_locate_with_edit_preroll(
2953 _views.begin()->view->region()->position() +
2954 _views.begin()->view->region()->length());
2958 if (!_editor->selection->selected (_primary)) {
2959 _primary->thaw_after_trim ();
2962 set<boost::shared_ptr<Playlist> > diffed_playlists;
2964 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2965 i->view->thaw_after_trim ();
2966 i->view->enable_display (true);
2968 /* Trimming one region may affect others on the playlist, so we need
2969 to get undo Commands from the whole playlist rather than just the
2970 region. Use diffed_playlists to make sure we don't diff a given
2971 playlist more than once.
2973 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2974 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2975 vector<Command*> cmds;
2977 _editor->session()->add_commands (cmds);
2978 diffed_playlists.insert (p);
2983 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2987 _editor->motion_frozen_playlists.clear ();
2988 _editor->commit_reversible_command();
2991 /* no mouse movement */
2992 _editor->point_trim (event, adjusted_current_frame (event));
2995 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2996 if (_operation == StartTrim) {
2997 i->view->trim_front_ending ();
3000 i->view->region()->resume_property_changes ();
3005 TrimDrag::aborted (bool movement_occurred)
3007 /* Our motion method is changing model state, so use the Undo system
3008 to cancel. Perhaps not ideal, as this will leave an Undo point
3009 behind which may be slightly odd from the user's point of view.
3014 if (movement_occurred) {
3018 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3019 i->view->region()->resume_property_changes ();
3024 TrimDrag::setup_pointer_frame_offset ()
3026 list<DraggingView>::iterator i = _views.begin ();
3027 while (i != _views.end() && i->view != _primary) {
3031 if (i == _views.end()) {
3035 switch (_operation) {
3037 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3040 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3047 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3051 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3052 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3057 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3059 Drag::start_grab (event, cursor);
3060 show_verbose_cursor_time (adjusted_current_frame(event));
3064 MeterMarkerDrag::setup_pointer_frame_offset ()
3066 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3070 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3072 if (!_marker->meter().movable()) {
3078 // create a dummy marker for visual representation of moving the
3079 // section, because whether its a copy or not, we're going to
3080 // leave or lose the original marker (leave if its a copy; lose if its
3081 // not, because we'll remove it from the map).
3083 MeterSection section (_marker->meter());
3085 if (!section.movable()) {
3090 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3092 _marker = new MeterMarker (
3094 *_editor->meter_group,
3095 UIConfiguration::instance().color ("meter marker"),
3097 *new MeterSection (_marker->meter())
3100 /* use the new marker for the grab */
3101 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3104 TempoMap& map (_editor->session()->tempo_map());
3105 /* get current state */
3106 before_state = &map.get_state();
3107 /* remove the section while we drag it */
3108 map.remove_meter (section, true);
3112 framepos_t const pf = adjusted_current_frame (event);
3114 _marker->set_position (pf);
3115 show_verbose_cursor_time (pf);
3119 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3121 if (!movement_occurred) {
3122 if (was_double_click()) {
3123 _editor->edit_meter_marker (*_marker);
3128 if (!_marker->meter().movable()) {
3132 motion (event, false);
3134 Timecode::BBT_Time when;
3136 TempoMap& map (_editor->session()->tempo_map());
3137 map.bbt_time (last_pointer_frame(), when);
3139 if (_copy == true) {
3140 _editor->begin_reversible_command (_("copy meter mark"));
3141 XMLNode &before = map.get_state();
3142 map.add_meter (_marker->meter(), when);
3143 XMLNode &after = map.get_state();
3144 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3145 _editor->commit_reversible_command ();
3148 _editor->begin_reversible_command (_("move meter mark"));
3150 /* we removed it before, so add it back now */
3152 map.add_meter (_marker->meter(), when);
3153 XMLNode &after = map.get_state();
3154 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3155 _editor->commit_reversible_command ();
3158 // delete the dummy marker we used for visual representation while moving.
3159 // a new visual marker will show up automatically.
3164 MeterMarkerDrag::aborted (bool moved)
3166 _marker->set_position (_marker->meter().frame ());
3169 TempoMap& map (_editor->session()->tempo_map());
3170 /* we removed it before, so add it back now */
3171 map.add_meter (_marker->meter(), _marker->meter().frame());
3172 // delete the dummy marker we used for visual representation while moving.
3173 // a new visual marker will show up automatically.
3178 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3182 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3184 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3189 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3191 Drag::start_grab (event, cursor);
3192 show_verbose_cursor_time (adjusted_current_frame (event));
3196 TempoMarkerDrag::setup_pointer_frame_offset ()
3198 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3202 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3204 if (!_marker->tempo().movable()) {
3210 // create a dummy marker for visual representation of moving the
3211 // section, because whether its a copy or not, we're going to
3212 // leave or lose the original marker (leave if its a copy; lose if its
3213 // not, because we'll remove it from the map).
3215 // create a dummy marker for visual representation of moving the copy.
3216 // The actual copying is not done before we reach the finish callback.
3219 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3221 TempoSection section (_marker->tempo());
3223 _marker = new TempoMarker (
3225 *_editor->tempo_group,
3226 UIConfiguration::instance().color ("tempo marker"),
3228 *new TempoSection (_marker->tempo())
3231 /* use the new marker for the grab */
3232 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3235 TempoMap& map (_editor->session()->tempo_map());
3236 /* get current state */
3237 before_state = &map.get_state();
3238 /* remove the section while we drag it */
3239 map.remove_tempo (section, true);
3243 framepos_t const pf = adjusted_current_frame (event);
3244 _marker->set_position (pf);
3245 show_verbose_cursor_time (pf);
3249 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3251 if (!movement_occurred) {
3252 if (was_double_click()) {
3253 _editor->edit_tempo_marker (*_marker);
3258 if (!_marker->tempo().movable()) {
3262 motion (event, false);
3264 TempoMap& map (_editor->session()->tempo_map());
3265 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3266 Timecode::BBT_Time when;
3268 map.bbt_time (beat_time, when);
3270 if (_copy == true) {
3271 _editor->begin_reversible_command (_("copy tempo mark"));
3272 XMLNode &before = map.get_state();
3273 map.add_tempo (_marker->tempo(), when);
3274 XMLNode &after = map.get_state();
3275 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3276 _editor->commit_reversible_command ();
3279 _editor->begin_reversible_command (_("move tempo mark"));
3280 /* we removed it before, so add it back now */
3281 map.add_tempo (_marker->tempo(), when);
3282 XMLNode &after = map.get_state();
3283 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3284 _editor->commit_reversible_command ();
3287 // delete the dummy marker we used for visual representation while moving.
3288 // a new visual marker will show up automatically.
3293 TempoMarkerDrag::aborted (bool moved)
3295 _marker->set_position (_marker->tempo().frame());
3297 TempoMap& map (_editor->session()->tempo_map());
3298 /* we removed it before, so add it back now */
3299 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3300 // delete the dummy marker we used for visual representation while moving.
3301 // a new visual marker will show up automatically.
3306 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3307 : Drag (e, &c.track_canvas_item(), false)
3311 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3314 /** Do all the things we do when dragging the playhead to make it look as though
3315 * we have located, without actually doing the locate (because that would cause
3316 * the diskstream buffers to be refilled, which is too slow).
3319 CursorDrag::fake_locate (framepos_t t)
3321 if (_editor->session () == 0) {
3325 _editor->playhead_cursor->set_position (t);
3327 Session* s = _editor->session ();
3328 if (s->timecode_transmission_suspended ()) {
3329 framepos_t const f = _editor->playhead_cursor->current_frame ();
3330 /* This is asynchronous so it will be sent "now"
3332 s->send_mmc_locate (f);
3333 /* These are synchronous and will be sent during the next
3336 s->queue_full_time_code ();
3337 s->queue_song_position_pointer ();
3340 show_verbose_cursor_time (t);
3341 _editor->UpdateAllTransportClocks (t);
3345 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3347 Drag::start_grab (event, c);
3348 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3350 _grab_zoom = _editor->samples_per_pixel;
3352 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3354 _editor->snap_to_with_modifier (where, event);
3356 _editor->_dragging_playhead = true;
3358 Session* s = _editor->session ();
3360 /* grab the track canvas item as well */
3362 _cursor.track_canvas_item().grab();
3365 if (_was_rolling && _stop) {
3369 if (s->is_auditioning()) {
3370 s->cancel_audition ();
3374 if (AudioEngine::instance()->connected()) {
3376 /* do this only if we're the engine is connected
3377 * because otherwise this request will never be
3378 * serviced and we'll busy wait forever. likewise,
3379 * notice if we are disconnected while waiting for the
3380 * request to be serviced.
3383 s->request_suspend_timecode_transmission ();
3384 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3385 /* twiddle our thumbs */
3390 fake_locate (where - snap_delta (event->button.state));
3394 CursorDrag::motion (GdkEvent* event, bool)
3396 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3397 _editor->snap_to_with_modifier (where, event);
3398 if (where != last_pointer_frame()) {
3399 fake_locate (where - snap_delta (event->button.state));
3404 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3406 _editor->_dragging_playhead = false;
3408 _cursor.track_canvas_item().ungrab();
3410 if (!movement_occurred && _stop) {
3414 motion (event, false);
3416 Session* s = _editor->session ();
3418 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3419 _editor->_pending_locate_request = true;
3420 s->request_resume_timecode_transmission ();
3425 CursorDrag::aborted (bool)
3427 _cursor.track_canvas_item().ungrab();
3429 if (_editor->_dragging_playhead) {
3430 _editor->session()->request_resume_timecode_transmission ();
3431 _editor->_dragging_playhead = false;
3434 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3437 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3438 : RegionDrag (e, i, p, v)
3440 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3444 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3446 Drag::start_grab (event, cursor);
3448 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3449 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3450 setup_snap_delta (r->position ());
3452 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3456 FadeInDrag::setup_pointer_frame_offset ()
3458 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3459 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3460 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3464 FadeInDrag::motion (GdkEvent* event, bool)
3466 framecnt_t fade_length;
3468 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3469 _editor->snap_to_with_modifier (pos, event);
3470 pos -= snap_delta (event->button.state);
3472 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3474 if (pos < (region->position() + 64)) {
3475 fade_length = 64; // this should be a minimum defined somewhere
3476 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3477 fade_length = region->length() - region->fade_out()->back()->when - 1;
3479 fade_length = pos - region->position();
3482 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3484 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3490 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3493 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3497 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3499 if (!movement_occurred) {
3503 framecnt_t fade_length;
3504 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3505 _editor->snap_to_with_modifier (pos, event);
3506 pos -= snap_delta (event->button.state);
3508 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3510 if (pos < (region->position() + 64)) {
3511 fade_length = 64; // this should be a minimum defined somewhere
3512 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3513 fade_length = region->length() - region->fade_out()->back()->when - 1;
3515 fade_length = pos - region->position();
3518 bool in_command = false;
3520 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3522 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3528 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3529 XMLNode &before = alist->get_state();
3531 tmp->audio_region()->set_fade_in_length (fade_length);
3532 tmp->audio_region()->set_fade_in_active (true);
3535 _editor->begin_reversible_command (_("change fade in length"));
3538 XMLNode &after = alist->get_state();
3539 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3543 _editor->commit_reversible_command ();
3548 FadeInDrag::aborted (bool)
3550 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3551 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3557 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3561 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3562 : RegionDrag (e, i, p, v)
3564 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3568 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3570 Drag::start_grab (event, cursor);
3572 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3573 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3574 setup_snap_delta (r->last_frame ());
3576 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3580 FadeOutDrag::setup_pointer_frame_offset ()
3582 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3583 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3584 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3588 FadeOutDrag::motion (GdkEvent* event, bool)
3590 framecnt_t fade_length;
3592 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3593 _editor->snap_to_with_modifier (pos, event);
3594 pos -= snap_delta (event->button.state);
3596 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3598 if (pos > (region->last_frame() - 64)) {
3599 fade_length = 64; // this should really be a minimum fade defined somewhere
3600 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3601 fade_length = region->length() - region->fade_in()->back()->when - 1;
3603 fade_length = region->last_frame() - pos;
3606 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3608 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3614 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3617 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3621 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3623 if (!movement_occurred) {
3627 framecnt_t fade_length;
3629 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3630 _editor->snap_to_with_modifier (pos, event);
3631 pos -= snap_delta (event->button.state);
3633 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3635 if (pos > (region->last_frame() - 64)) {
3636 fade_length = 64; // this should really be a minimum fade defined somewhere
3637 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3638 fade_length = region->length() - region->fade_in()->back()->when - 1;
3640 fade_length = region->last_frame() - pos;
3643 bool in_command = false;
3645 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3647 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3653 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3654 XMLNode &before = alist->get_state();
3656 tmp->audio_region()->set_fade_out_length (fade_length);
3657 tmp->audio_region()->set_fade_out_active (true);
3660 _editor->begin_reversible_command (_("change fade out length"));
3663 XMLNode &after = alist->get_state();
3664 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3668 _editor->commit_reversible_command ();
3673 FadeOutDrag::aborted (bool)
3675 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3676 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3682 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3686 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3689 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3691 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3694 _points.push_back (ArdourCanvas::Duple (0, 0));
3695 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3698 MarkerDrag::~MarkerDrag ()
3700 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3705 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3707 location = new Location (*l);
3708 markers.push_back (m);
3713 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3715 Drag::start_grab (event, cursor);
3719 Location *location = _editor->find_location_from_marker (_marker, is_start);
3720 _editor->_dragging_edit_point = true;
3722 update_item (location);
3724 // _drag_line->show();
3725 // _line->raise_to_top();
3728 show_verbose_cursor_time (location->start());
3730 show_verbose_cursor_time (location->end());
3733 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3736 case Selection::Toggle:
3737 /* we toggle on the button release */
3739 case Selection::Set:
3740 if (!_editor->selection->selected (_marker)) {
3741 _editor->selection->set (_marker);
3744 case Selection::Extend:
3746 Locations::LocationList ll;
3747 list<ArdourMarker*> to_add;
3749 _editor->selection->markers.range (s, e);
3750 s = min (_marker->position(), s);
3751 e = max (_marker->position(), e);
3754 if (e < max_framepos) {
3757 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3758 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3759 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3762 to_add.push_back (lm->start);
3765 to_add.push_back (lm->end);
3769 if (!to_add.empty()) {
3770 _editor->selection->add (to_add);
3774 case Selection::Add:
3775 _editor->selection->add (_marker);
3779 /* Set up copies for us to manipulate during the drag
3782 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3784 Location* l = _editor->find_location_from_marker (*i, is_start);
3791 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3793 /* range: check that the other end of the range isn't
3796 CopiedLocationInfo::iterator x;
3797 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3798 if (*(*x).location == *l) {
3802 if (x == _copied_locations.end()) {
3803 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3805 (*x).markers.push_back (*i);
3806 (*x).move_both = true;
3814 MarkerDrag::setup_pointer_frame_offset ()
3817 Location *location = _editor->find_location_from_marker (_marker, is_start);
3818 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3822 MarkerDrag::motion (GdkEvent* event, bool)
3824 framecnt_t f_delta = 0;
3826 bool move_both = false;
3827 Location *real_location;
3828 Location *copy_location = 0;
3830 framepos_t const newframe = adjusted_current_frame (event);
3831 framepos_t next = newframe;
3833 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
3837 CopiedLocationInfo::iterator x;
3839 /* find the marker we're dragging, and compute the delta */
3841 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3843 copy_location = (*x).location;
3845 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3847 /* this marker is represented by this
3848 * CopiedLocationMarkerInfo
3851 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3856 if (real_location->is_mark()) {
3857 f_delta = newframe - copy_location->start();
3861 switch (_marker->type()) {
3862 case ArdourMarker::SessionStart:
3863 case ArdourMarker::RangeStart:
3864 case ArdourMarker::LoopStart:
3865 case ArdourMarker::PunchIn:
3866 f_delta = newframe - copy_location->start();
3869 case ArdourMarker::SessionEnd:
3870 case ArdourMarker::RangeEnd:
3871 case ArdourMarker::LoopEnd:
3872 case ArdourMarker::PunchOut:
3873 f_delta = newframe - copy_location->end();
3876 /* what kind of marker is this ? */
3885 if (x == _copied_locations.end()) {
3886 /* hmm, impossible - we didn't find the dragged marker */
3890 /* now move them all */
3892 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3894 copy_location = x->location;
3896 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3900 if (real_location->locked()) {
3904 if (copy_location->is_mark()) {
3908 copy_location->set_start (copy_location->start() + f_delta);
3912 framepos_t new_start = copy_location->start() + f_delta;
3913 framepos_t new_end = copy_location->end() + f_delta;
3915 if (is_start) { // start-of-range marker
3917 if (move_both || (*x).move_both) {
3918 copy_location->set_start (new_start);
3919 copy_location->set_end (new_end);
3920 } else if (new_start < copy_location->end()) {
3921 copy_location->set_start (new_start);
3922 } else if (newframe > 0) {
3923 _editor->snap_to (next, RoundUpAlways, true);
3924 copy_location->set_end (next);
3925 copy_location->set_start (newframe);
3928 } else { // end marker
3930 if (move_both || (*x).move_both) {
3931 copy_location->set_end (new_end);
3932 copy_location->set_start (new_start);
3933 } else if (new_end > copy_location->start()) {
3934 copy_location->set_end (new_end);
3935 } else if (newframe > 0) {
3936 _editor->snap_to (next, RoundDownAlways, true);
3937 copy_location->set_start (next);
3938 copy_location->set_end (newframe);
3943 update_item (copy_location);
3945 /* now lookup the actual GUI items used to display this
3946 * location and move them to wherever the copy of the location
3947 * is now. This means that the logic in ARDOUR::Location is
3948 * still enforced, even though we are not (yet) modifying
3949 * the real Location itself.
3952 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3955 lm->set_position (copy_location->start(), copy_location->end());
3960 assert (!_copied_locations.empty());
3962 show_verbose_cursor_time (newframe);
3966 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3968 if (!movement_occurred) {
3970 if (was_double_click()) {
3971 _editor->rename_marker (_marker);
3975 /* just a click, do nothing but finish
3976 off the selection process
3979 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3982 case Selection::Set:
3983 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3984 _editor->selection->set (_marker);
3988 case Selection::Toggle:
3989 /* we toggle on the button release, click only */
3990 _editor->selection->toggle (_marker);
3993 case Selection::Extend:
3994 case Selection::Add:
4001 _editor->_dragging_edit_point = false;
4003 XMLNode &before = _editor->session()->locations()->get_state();
4004 bool in_command = false;
4006 MarkerSelection::iterator i;
4007 CopiedLocationInfo::iterator x;
4010 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4011 x != _copied_locations.end() && i != _editor->selection->markers.end();
4014 Location * location = _editor->find_location_from_marker (*i, is_start);
4018 if (location->locked()) {
4022 _editor->begin_reversible_command ( _("move marker") );
4025 if (location->is_mark()) {
4026 location->set_start (((*x).location)->start());
4028 location->set (((*x).location)->start(), ((*x).location)->end());
4034 XMLNode &after = _editor->session()->locations()->get_state();
4035 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4036 _editor->commit_reversible_command ();
4041 MarkerDrag::aborted (bool movement_occured)
4043 if (!movement_occured) {
4047 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4049 /* move all markers to their original location */
4052 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4055 Location * location = _editor->find_location_from_marker (*m, is_start);
4058 (*m)->set_position (is_start ? location->start() : location->end());
4065 MarkerDrag::update_item (Location*)
4070 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4072 _cumulative_x_drag (0)
4073 , _cumulative_y_drag (0)
4076 if (_zero_gain_fraction < 0.0) {
4077 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4080 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4082 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4088 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4090 Drag::start_grab (event, _editor->cursors()->fader);
4092 // start the grab at the center of the control point so
4093 // the point doesn't 'jump' to the mouse after the first drag
4094 _fixed_grab_x = _point->get_x();
4095 _fixed_grab_y = _point->get_y();
4097 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4098 setup_snap_delta (pos);
4100 float const fraction = 1 - (_point->get_y() / _point->line().height());
4101 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4103 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4105 if (!_point->can_slide ()) {
4106 _x_constrained = true;
4111 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4113 double dx = _drags->current_pointer_x() - last_pointer_x();
4114 double dy = current_pointer_y() - last_pointer_y();
4116 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4121 /* coordinate in pixels relative to the start of the region (for region-based automation)
4122 or track (for track-based automation) */
4123 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4124 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4126 // calculate zero crossing point. back off by .01 to stay on the
4127 // positive side of zero
4128 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4130 if (_x_constrained) {
4133 if (_y_constrained) {
4137 _cumulative_x_drag = cx - _fixed_grab_x;
4138 _cumulative_y_drag = cy - _fixed_grab_y;
4140 // make sure we hit zero when passing through
4141 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4147 cy = min ((double) _point->line().height(), cy);
4149 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4151 if (!_x_constrained) {
4152 _editor->snap_to_with_modifier (cx_frames, event);
4155 cx_frames -= snap_delta (event->button.state);
4156 cx_frames = min (cx_frames, _point->line().maximum_time());
4158 float const fraction = 1.0 - (cy / _point->line().height());
4161 _editor->begin_reversible_command (_("automation event move"));
4162 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
4165 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4167 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4171 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4173 if (!movement_occurred) {
4176 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4177 _editor->reset_point_selection ();
4181 motion (event, false);
4182 _point->line().end_drag (_pushing, _final_index);
4183 _editor->commit_reversible_command ();
4188 ControlPointDrag::aborted (bool)
4190 _point->line().reset ();
4194 ControlPointDrag::active (Editing::MouseMode m)
4196 if (m == Editing::MouseDraw) {
4197 /* always active in mouse draw */
4201 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4202 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4205 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4208 , _cumulative_y_drag (0)
4212 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4216 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4218 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4221 _item = &_line->grab_item ();
4223 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4224 origin, and ditto for y.
4227 double cx = event->button.x;
4228 double cy = event->button.y;
4230 _line->parent_group().canvas_to_item (cx, cy);
4232 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4234 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4235 /* no adjacent points */
4239 Drag::start_grab (event, _editor->cursors()->fader);
4241 /* store grab start in parent frame */
4246 double fraction = 1.0 - (cy / _line->height());
4248 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4252 LineDrag::motion (GdkEvent* event, bool first_move)
4254 double dy = current_pointer_y() - last_pointer_y();
4256 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4260 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4262 _cumulative_y_drag = cy - _fixed_grab_y;
4265 cy = min ((double) _line->height(), cy);
4267 double const fraction = 1.0 - (cy / _line->height());
4271 _editor->begin_reversible_command (_("automation range move"));
4272 _line->start_drag_line (_before, _after, fraction);
4275 /* we are ignoring x position for this drag, so we can just pass in anything */
4276 _line->drag_motion (0, fraction, true, false, ignored);
4278 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4282 LineDrag::finished (GdkEvent* event, bool movement_occured)
4284 if (movement_occured) {
4285 motion (event, false);
4286 _line->end_drag (false, 0);
4287 _editor->commit_reversible_command ();
4289 /* add a new control point on the line */
4291 AutomationTimeAxisView* atv;
4293 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4294 framepos_t where = _editor->canvas_event_sample (event, 0, 0);
4296 atv->add_automation_event (event, where, event->button.y, false);
4297 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4298 AudioRegionView* arv;
4300 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4301 arv->add_gain_point_event (arv->get_canvas_group (), event, false);
4308 LineDrag::aborted (bool)
4313 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4316 _cumulative_x_drag (0)
4318 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4322 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4324 Drag::start_grab (event);
4326 _line = reinterpret_cast<Line*> (_item);
4329 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4331 double cx = event->button.x;
4332 double cy = event->button.y;
4334 _item->parent()->canvas_to_item (cx, cy);
4336 /* store grab start in parent frame */
4337 _region_view_grab_x = cx;
4339 _before = *(float*) _item->get_data ("position");
4341 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4343 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4347 FeatureLineDrag::motion (GdkEvent*, bool)
4349 double dx = _drags->current_pointer_x() - last_pointer_x();
4351 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4353 _cumulative_x_drag += dx;
4355 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4364 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4366 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4368 float *pos = new float;
4371 _line->set_data ("position", pos);
4377 FeatureLineDrag::finished (GdkEvent*, bool)
4379 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4380 _arv->update_transient(_before, _before);
4384 FeatureLineDrag::aborted (bool)
4389 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4391 , _vertical_only (false)
4393 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4397 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4399 Drag::start_grab (event);
4400 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4404 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4411 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4413 framepos_t grab = grab_frame ();
4414 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4415 _editor->snap_to_with_modifier (grab, event);
4417 grab = raw_grab_frame ();
4420 /* base start and end on initial click position */
4430 if (current_pointer_y() < grab_y()) {
4431 y1 = current_pointer_y();
4434 y2 = current_pointer_y();
4438 if (start != end || y1 != y2) {
4440 double x1 = _editor->sample_to_pixel (start);
4441 double x2 = _editor->sample_to_pixel (end);
4442 const double min_dimension = 2.0;
4444 if (_vertical_only) {
4445 /* fixed 10 pixel width */
4449 x2 = min (x1 - min_dimension, x2);
4451 x2 = max (x1 + min_dimension, x2);
4456 y2 = min (y1 - min_dimension, y2);
4458 y2 = max (y1 + min_dimension, y2);
4461 /* translate rect into item space and set */
4463 ArdourCanvas::Rect r (x1, y1, x2, y2);
4465 /* this drag is a _trackview_only == true drag, so the y1 and
4466 * y2 (computed using current_pointer_y() and grab_y()) will be
4467 * relative to the top of the trackview group). The
4468 * rubberband rect has the same parent/scroll offset as the
4469 * the trackview group, so we can use the "r" rect directly
4470 * to set the shape of the rubberband.
4473 _editor->rubberband_rect->set (r);
4474 _editor->rubberband_rect->show();
4475 _editor->rubberband_rect->raise_to_top();
4477 show_verbose_cursor_time (pf);
4479 do_select_things (event, true);
4484 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4488 framepos_t grab = grab_frame ();
4489 framepos_t lpf = last_pointer_frame ();
4491 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4492 grab = raw_grab_frame ();
4493 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4507 if (current_pointer_y() < grab_y()) {
4508 y1 = current_pointer_y();
4511 y2 = current_pointer_y();
4515 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4519 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4521 if (movement_occurred) {
4523 motion (event, false);
4524 do_select_things (event, false);
4530 bool do_deselect = true;
4531 MidiTimeAxisView* mtv;
4533 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4535 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4536 /* nothing selected */
4537 add_midi_region (mtv);
4538 do_deselect = false;
4542 /* do not deselect if Primary or Tertiary (toggle-select or
4543 * extend-select are pressed.
4546 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4547 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4554 _editor->rubberband_rect->hide();
4558 RubberbandSelectDrag::aborted (bool)
4560 _editor->rubberband_rect->hide ();
4563 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4564 : RegionDrag (e, i, p, v)
4566 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4570 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4572 Drag::start_grab (event, cursor);
4574 _editor->get_selection().add (_primary);
4576 framepos_t where = _primary->region()->position();
4577 setup_snap_delta (where);
4579 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4583 TimeFXDrag::motion (GdkEvent* event, bool)
4585 RegionView* rv = _primary;
4586 StreamView* cv = rv->get_time_axis_view().view ();
4588 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4589 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4590 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4591 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4592 _editor->snap_to_with_modifier (pf, event);
4593 pf -= snap_delta (event->button.state);
4595 if (pf > rv->region()->position()) {
4596 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4599 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4603 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4605 _primary->get_time_axis_view().hide_timestretch ();
4607 if (!movement_occurred) {
4611 if (last_pointer_frame() < _primary->region()->position()) {
4612 /* backwards drag of the left edge - not usable */
4616 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4618 float percentage = (double) newlen / (double) _primary->region()->length();
4620 #ifndef USE_RUBBERBAND
4621 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4622 if (_primary->region()->data_type() == DataType::AUDIO) {
4623 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4627 if (!_editor->get_selection().regions.empty()) {
4628 /* primary will already be included in the selection, and edit
4629 group shared editing will propagate selection across
4630 equivalent regions, so just use the current region
4634 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4635 error << _("An error occurred while executing time stretch operation") << endmsg;
4641 TimeFXDrag::aborted (bool)
4643 _primary->get_time_axis_view().hide_timestretch ();
4646 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4649 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4653 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4655 Drag::start_grab (event);
4659 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4661 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4665 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4667 if (movement_occurred && _editor->session()) {
4668 /* make sure we stop */
4669 _editor->session()->request_transport_speed (0.0);
4674 ScrubDrag::aborted (bool)
4679 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4683 , _time_selection_at_start (!_editor->get_selection().time.empty())
4685 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4687 if (_time_selection_at_start) {
4688 start_at_start = _editor->get_selection().time.start();
4689 end_at_start = _editor->get_selection().time.end_frame();
4694 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4696 if (_editor->session() == 0) {
4700 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4702 switch (_operation) {
4703 case CreateSelection:
4704 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4709 cursor = _editor->cursors()->selector;
4710 Drag::start_grab (event, cursor);
4713 case SelectionStartTrim:
4714 if (_editor->clicked_axisview) {
4715 _editor->clicked_axisview->order_selection_trims (_item, true);
4717 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4720 case SelectionEndTrim:
4721 if (_editor->clicked_axisview) {
4722 _editor->clicked_axisview->order_selection_trims (_item, false);
4724 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4728 Drag::start_grab (event, cursor);
4731 case SelectionExtend:
4732 Drag::start_grab (event, cursor);
4736 if (_operation == SelectionMove) {
4737 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4739 show_verbose_cursor_time (adjusted_current_frame (event));
4744 SelectionDrag::setup_pointer_frame_offset ()
4746 switch (_operation) {
4747 case CreateSelection:
4748 _pointer_frame_offset = 0;
4751 case SelectionStartTrim:
4753 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4756 case SelectionEndTrim:
4757 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4760 case SelectionExtend:
4766 SelectionDrag::motion (GdkEvent* event, bool first_move)
4768 framepos_t start = 0;
4770 framecnt_t length = 0;
4771 framecnt_t distance = 0;
4773 framepos_t const pending_position = adjusted_current_frame (event);
4775 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4779 switch (_operation) {
4780 case CreateSelection:
4782 framepos_t grab = grab_frame ();
4785 grab = adjusted_current_frame (event, false);
4786 if (grab < pending_position) {
4787 _editor->snap_to (grab, RoundDownMaybe);
4789 _editor->snap_to (grab, RoundUpMaybe);
4793 if (pending_position < grab) {
4794 start = pending_position;
4797 end = pending_position;
4801 /* first drag: Either add to the selection
4802 or create a new selection
4809 /* adding to the selection */
4810 _editor->set_selected_track_as_side_effect (Selection::Add);
4811 _editor->clicked_selection = _editor->selection->add (start, end);
4818 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4819 _editor->set_selected_track_as_side_effect (Selection::Set);
4822 _editor->clicked_selection = _editor->selection->set (start, end);
4826 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4827 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4828 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4830 _editor->selection->add (atest);
4834 /* select all tracks within the rectangle that we've marked out so far */
4835 TrackViewList new_selection;
4836 TrackViewList& all_tracks (_editor->track_views);
4838 ArdourCanvas::Coord const top = grab_y();
4839 ArdourCanvas::Coord const bottom = current_pointer_y();
4841 if (top >= 0 && bottom >= 0) {
4843 //first, find the tracks that are covered in the y range selection
4844 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4845 if ((*i)->covered_by_y_range (top, bottom)) {
4846 new_selection.push_back (*i);
4850 //now find any tracks that are GROUPED with the tracks we selected
4851 TrackViewList grouped_add = new_selection;
4852 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4853 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4854 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4855 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4856 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4857 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4858 grouped_add.push_back (*j);
4863 //now compare our list with the current selection, and add or remove as necessary
4864 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4865 TrackViewList tracks_to_add;
4866 TrackViewList tracks_to_remove;
4867 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4868 if ( !_editor->selection->tracks.contains ( *i ) )
4869 tracks_to_add.push_back ( *i );
4870 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4871 if ( !grouped_add.contains ( *i ) )
4872 tracks_to_remove.push_back ( *i );
4873 _editor->selection->add(tracks_to_add);
4874 _editor->selection->remove(tracks_to_remove);
4880 case SelectionStartTrim:
4882 end = _editor->selection->time[_editor->clicked_selection].end;
4884 if (pending_position > end) {
4887 start = pending_position;
4891 case SelectionEndTrim:
4893 start = _editor->selection->time[_editor->clicked_selection].start;
4895 if (pending_position < start) {
4898 end = pending_position;
4905 start = _editor->selection->time[_editor->clicked_selection].start;
4906 end = _editor->selection->time[_editor->clicked_selection].end;
4908 length = end - start;
4909 distance = pending_position - start;
4910 start = pending_position;
4911 _editor->snap_to (start);
4913 end = start + length;
4917 case SelectionExtend:
4922 switch (_operation) {
4924 if (_time_selection_at_start) {
4925 _editor->selection->move_time (distance);
4929 _editor->selection->replace (_editor->clicked_selection, start, end);
4933 if (_operation == SelectionMove) {
4934 show_verbose_cursor_time(start);
4936 show_verbose_cursor_time(pending_position);
4941 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4943 Session* s = _editor->session();
4945 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4946 if (movement_occurred) {
4947 motion (event, false);
4948 /* XXX this is not object-oriented programming at all. ick */
4949 if (_editor->selection->time.consolidate()) {
4950 _editor->selection->TimeChanged ();
4953 /* XXX what if its a music time selection? */
4955 if (s->get_play_range() && s->transport_rolling()) {
4956 s->request_play_range (&_editor->selection->time, true);
4958 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
4959 if (_operation == SelectionEndTrim)
4960 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4962 s->request_locate (_editor->get_selection().time.start());
4966 if (_editor->get_selection().time.length() != 0) {
4967 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
4969 s->clear_range_selection ();
4974 /* just a click, no pointer movement.
4977 if (_operation == SelectionExtend) {
4978 if (_time_selection_at_start) {
4979 framepos_t pos = adjusted_current_frame (event, false);
4980 framepos_t start = min (pos, start_at_start);
4981 framepos_t end = max (pos, end_at_start);
4982 _editor->selection->set (start, end);
4985 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4986 if (_editor->clicked_selection) {
4987 _editor->selection->remove (_editor->clicked_selection);
4990 if (!_editor->clicked_selection) {
4991 _editor->selection->clear_time();
4996 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4997 _editor->selection->set (_editor->clicked_axisview);
5000 if (s && s->get_play_range () && s->transport_rolling()) {
5001 s->request_stop (false, false);
5006 _editor->stop_canvas_autoscroll ();
5007 _editor->clicked_selection = 0;
5008 _editor->commit_reversible_selection_op ();
5012 SelectionDrag::aborted (bool)
5017 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5018 : Drag (e, i, false),
5022 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5024 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5025 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5026 physical_screen_height (_editor->get_window())));
5027 _drag_rect->hide ();
5029 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5030 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5033 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5035 /* normal canvas items will be cleaned up when their parent group is deleted. But
5036 this item is created as the child of a long-lived parent group, and so we
5037 need to explicitly delete it.
5043 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5045 if (_editor->session() == 0) {
5049 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5051 if (!_editor->temp_location) {
5052 _editor->temp_location = new Location (*_editor->session());
5055 switch (_operation) {
5056 case CreateSkipMarker:
5057 case CreateRangeMarker:
5058 case CreateTransportMarker:
5059 case CreateCDMarker:
5061 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5066 cursor = _editor->cursors()->selector;
5070 Drag::start_grab (event, cursor);
5072 show_verbose_cursor_time (adjusted_current_frame (event));
5076 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5078 framepos_t start = 0;
5080 ArdourCanvas::Rectangle *crect;
5082 switch (_operation) {
5083 case CreateSkipMarker:
5084 crect = _editor->range_bar_drag_rect;
5086 case CreateRangeMarker:
5087 crect = _editor->range_bar_drag_rect;
5089 case CreateTransportMarker:
5090 crect = _editor->transport_bar_drag_rect;
5092 case CreateCDMarker:
5093 crect = _editor->cd_marker_bar_drag_rect;
5096 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5101 framepos_t const pf = adjusted_current_frame (event);
5103 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5104 framepos_t grab = grab_frame ();
5105 _editor->snap_to (grab);
5107 if (pf < grab_frame()) {
5115 /* first drag: Either add to the selection
5116 or create a new selection.
5121 _editor->temp_location->set (start, end);
5125 update_item (_editor->temp_location);
5127 //_drag_rect->raise_to_top();
5133 _editor->temp_location->set (start, end);
5135 double x1 = _editor->sample_to_pixel (start);
5136 double x2 = _editor->sample_to_pixel (end);
5140 update_item (_editor->temp_location);
5143 show_verbose_cursor_time (pf);
5148 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5150 Location * newloc = 0;
5154 if (movement_occurred) {
5155 motion (event, false);
5158 switch (_operation) {
5159 case CreateSkipMarker:
5160 case CreateRangeMarker:
5161 case CreateCDMarker:
5163 XMLNode &before = _editor->session()->locations()->get_state();
5164 if (_operation == CreateSkipMarker) {
5165 _editor->begin_reversible_command (_("new skip marker"));
5166 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5167 flags = Location::IsRangeMarker | Location::IsSkip;
5168 _editor->range_bar_drag_rect->hide();
5169 } else if (_operation == CreateCDMarker) {
5170 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5171 _editor->begin_reversible_command (_("new CD marker"));
5172 flags = Location::IsRangeMarker | Location::IsCDMarker;
5173 _editor->cd_marker_bar_drag_rect->hide();
5175 _editor->begin_reversible_command (_("new skip marker"));
5176 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5177 flags = Location::IsRangeMarker;
5178 _editor->range_bar_drag_rect->hide();
5180 newloc = new Location (
5181 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5184 _editor->session()->locations()->add (newloc, true);
5185 XMLNode &after = _editor->session()->locations()->get_state();
5186 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5187 _editor->commit_reversible_command ();
5191 case CreateTransportMarker:
5192 // popup menu to pick loop or punch
5193 _editor->new_transport_marker_context_menu (&event->button, _item);
5199 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5201 if (_operation == CreateTransportMarker) {
5203 /* didn't drag, so just locate */
5205 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5207 } else if (_operation == CreateCDMarker) {
5209 /* didn't drag, but mark is already created so do
5212 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5217 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5219 if (end == max_framepos) {
5220 end = _editor->session()->current_end_frame ();
5223 if (start == max_framepos) {
5224 start = _editor->session()->current_start_frame ();
5227 switch (_editor->mouse_mode) {
5229 /* find the two markers on either side and then make the selection from it */
5230 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5234 /* find the two markers on either side of the click and make the range out of it */
5235 _editor->selection->set (start, end);
5244 _editor->stop_canvas_autoscroll ();
5248 RangeMarkerBarDrag::aborted (bool movement_occured)
5250 if (movement_occured) {
5251 _drag_rect->hide ();
5256 RangeMarkerBarDrag::update_item (Location* location)
5258 double const x1 = _editor->sample_to_pixel (location->start());
5259 double const x2 = _editor->sample_to_pixel (location->end());
5261 _drag_rect->set_x0 (x1);
5262 _drag_rect->set_x1 (x2);
5265 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5267 , _cumulative_dx (0)
5268 , _cumulative_dy (0)
5270 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5272 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5274 _region = &_primary->region_view ();
5275 _note_height = _region->midi_stream_view()->note_height ();
5279 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5281 Drag::start_grab (event);
5282 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5284 if (!(_was_selected = _primary->selected())) {
5286 /* tertiary-click means extend selection - we'll do that on button release,
5287 so don't add it here, because otherwise we make it hard to figure
5288 out the "extend-to" range.
5291 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5294 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5297 _region->note_selected (_primary, true);
5299 _region->unique_select (_primary);
5302 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5303 _editor->commit_reversible_selection_op();
5308 /** @return Current total drag x change in frames */
5310 NoteDrag::total_dx (const guint state) const
5313 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5315 /* primary note time */
5316 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5318 /* new time of the primary note in session frames */
5319 frameoffset_t st = n + dx + snap_delta (state);
5321 framepos_t const rp = _region->region()->position ();
5323 /* prevent the note being dragged earlier than the region's position */
5326 /* possibly snap and return corresponding delta */
5330 if (ArdourKeyboard::indicates_snap (state)) {
5331 if (_editor->snap_mode () != SnapOff) {
5335 if (_editor->snap_mode () == SnapOff) {
5337 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5338 if (ArdourKeyboard::indicates_snap_delta (state)) {
5346 ret = _region->snap_frame_to_frame (st - rp) + rp - n - snap_delta (state);
5348 ret = st - n - snap_delta (state);
5353 /** @return Current total drag y change in note number */
5355 NoteDrag::total_dy () const
5357 MidiStreamView* msv = _region->midi_stream_view ();
5358 double const y = _region->midi_view()->y_position ();
5359 /* new current note */
5360 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5362 n = max (msv->lowest_note(), n);
5363 n = min (msv->highest_note(), n);
5364 /* and work out delta */
5365 return n - msv->y_to_note (grab_y() - y);
5369 NoteDrag::motion (GdkEvent * event, bool)
5371 /* Total change in x and y since the start of the drag */
5372 frameoffset_t const dx = total_dx (event->button.state);
5373 int8_t const dy = total_dy ();
5375 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5376 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5377 double const tdy = -dy * _note_height - _cumulative_dy;
5380 _cumulative_dx += tdx;
5381 _cumulative_dy += tdy;
5383 int8_t note_delta = total_dy();
5385 _region->move_selection (tdx, tdy, note_delta);
5387 /* the new note value may be the same as the old one, but we
5388 * don't know what that means because the selection may have
5389 * involved more than one note and we might be doing something
5390 * odd with them. so show the note value anyway, always.
5394 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5396 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5397 (int) floor ((double)new_note));
5399 show_verbose_cursor_text (buf);
5404 NoteDrag::finished (GdkEvent* ev, bool moved)
5407 /* no motion - select note */
5409 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5410 _editor->current_mouse_mode() == Editing::MouseDraw) {
5412 bool changed = false;
5414 if (_was_selected) {
5415 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5417 _region->note_deselected (_primary);
5421 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5422 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5424 if (!extend && !add && _region->selection_size() > 1) {
5425 _region->unique_select (_primary);
5427 } else if (extend) {
5428 _region->note_selected (_primary, true, true);
5431 /* it was added during button press */
5436 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5437 _editor->commit_reversible_selection_op();
5441 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5446 NoteDrag::aborted (bool)
5451 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5452 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5453 : Drag (editor, atv->base_item ())
5455 , _y_origin (atv->y_position())
5456 , _nothing_to_drag (false)
5458 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5459 setup (atv->lines ());
5462 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5463 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5464 : Drag (editor, rv->get_canvas_group ())
5466 , _y_origin (rv->get_time_axis_view().y_position())
5467 , _nothing_to_drag (false)
5470 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5472 list<boost::shared_ptr<AutomationLine> > lines;
5474 AudioRegionView* audio_view;
5475 AutomationRegionView* automation_view;
5476 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5477 lines.push_back (audio_view->get_gain_line ());
5478 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5479 lines.push_back (automation_view->line ());
5482 error << _("Automation range drag created for invalid region type") << endmsg;
5488 /** @param lines AutomationLines to drag.
5489 * @param offset Offset from the session start to the points in the AutomationLines.
5492 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5494 /* find the lines that overlap the ranges being dragged */
5495 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5496 while (i != lines.end ()) {
5497 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5500 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5502 /* check this range against all the AudioRanges that we are using */
5503 list<AudioRange>::const_iterator k = _ranges.begin ();
5504 while (k != _ranges.end()) {
5505 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5511 /* add it to our list if it overlaps at all */
5512 if (k != _ranges.end()) {
5517 _lines.push_back (n);
5523 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5527 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5529 return 1.0 - ((global_y - _y_origin) / line->height());
5533 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5535 const double v = list->eval(x);
5536 return _integral ? rint(v) : v;
5540 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5542 Drag::start_grab (event, cursor);
5544 /* Get line states before we start changing things */
5545 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5546 i->state = &i->line->get_state ();
5547 i->original_fraction = y_fraction (i->line, current_pointer_y());
5550 if (_ranges.empty()) {
5552 /* No selected time ranges: drag all points */
5553 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5554 uint32_t const N = i->line->npoints ();
5555 for (uint32_t j = 0; j < N; ++j) {
5556 i->points.push_back (i->line->nth (j));
5562 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5564 framecnt_t const half = (i->start + i->end) / 2;
5566 /* find the line that this audio range starts in */
5567 list<Line>::iterator j = _lines.begin();
5568 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5572 if (j != _lines.end()) {
5573 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5575 /* j is the line that this audio range starts in; fade into it;
5576 64 samples length plucked out of thin air.
5579 framepos_t a = i->start + 64;
5584 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5585 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5587 the_list->editor_add (p, value (the_list, p), false);
5588 the_list->editor_add (q, value (the_list, q), false);
5591 /* same thing for the end */
5594 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5598 if (j != _lines.end()) {
5599 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5601 /* j is the line that this audio range starts in; fade out of it;
5602 64 samples length plucked out of thin air.
5605 framepos_t b = i->end - 64;
5610 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5611 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5613 the_list->editor_add (p, value (the_list, p), false);
5614 the_list->editor_add (q, value (the_list, q), false);
5618 _nothing_to_drag = true;
5620 /* Find all the points that should be dragged and put them in the relevant
5621 points lists in the Line structs.
5624 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5626 uint32_t const N = i->line->npoints ();
5627 for (uint32_t j = 0; j < N; ++j) {
5629 /* here's a control point on this line */
5630 ControlPoint* p = i->line->nth (j);
5631 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5633 /* see if it's inside a range */
5634 list<AudioRange>::const_iterator k = _ranges.begin ();
5635 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5639 if (k != _ranges.end()) {
5640 /* dragging this point */
5641 _nothing_to_drag = false;
5642 i->points.push_back (p);
5648 if (_nothing_to_drag) {
5654 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5656 if (_nothing_to_drag) {
5661 _editor->begin_reversible_command (_("automation range move"));
5662 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5663 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5667 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5668 float const f = y_fraction (l->line, current_pointer_y());
5669 /* we are ignoring x position for this drag, so we can just pass in anything */
5671 l->line->drag_motion (0, f, true, false, ignored);
5672 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5677 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5679 if (_nothing_to_drag || !motion_occurred) {
5683 motion (event, false);
5684 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5685 i->line->end_drag (false, 0);
5688 _editor->commit_reversible_command ();
5692 AutomationRangeDrag::aborted (bool)
5694 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5699 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5701 , initial_time_axis_view (itav)
5703 /* note that time_axis_view may be null if the regionview was created
5704 * as part of a copy operation.
5706 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5707 layer = v->region()->layer ();
5708 initial_y = v->get_canvas_group()->position().y;
5709 initial_playlist = v->region()->playlist ();
5710 initial_position = v->region()->position ();
5711 initial_end = v->region()->position () + v->region()->length ();
5714 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5715 : Drag (e, i->canvas_item ())
5718 , _cumulative_dx (0)
5720 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5721 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5726 PatchChangeDrag::motion (GdkEvent* ev, bool)
5728 framepos_t f = adjusted_current_frame (ev);
5729 boost::shared_ptr<Region> r = _region_view->region ();
5730 f = max (f, r->position ());
5731 f = min (f, r->last_frame ());
5733 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5734 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5735 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5736 _cumulative_dx = dxu;
5740 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5742 if (!movement_occurred) {
5746 boost::shared_ptr<Region> r (_region_view->region ());
5747 framepos_t f = adjusted_current_frame (ev);
5748 f = max (f, r->position ());
5749 f = min (f, r->last_frame ());
5751 _region_view->move_patch_change (
5753 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5758 PatchChangeDrag::aborted (bool)
5760 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5764 PatchChangeDrag::setup_pointer_frame_offset ()
5766 boost::shared_ptr<Region> region = _region_view->region ();
5767 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5770 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5771 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5778 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5780 _region_view->update_drag_selection (
5782 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5786 MidiRubberbandSelectDrag::deselect_things ()
5791 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5792 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5795 _vertical_only = true;
5799 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5801 double const y = _region_view->midi_view()->y_position ();
5803 y1 = max (0.0, y1 - y);
5804 y2 = max (0.0, y2 - y);
5806 _region_view->update_vertical_drag_selection (
5809 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5814 MidiVerticalSelectDrag::deselect_things ()
5819 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5820 : RubberbandSelectDrag (e, i)
5826 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5828 if (drag_in_progress) {
5829 /* We just want to select things at the end of the drag, not during it */
5833 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5835 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5837 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5839 _editor->commit_reversible_selection_op ();
5843 EditorRubberbandSelectDrag::deselect_things ()
5845 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5847 _editor->selection->clear_tracks();
5848 _editor->selection->clear_regions();
5849 _editor->selection->clear_points ();
5850 _editor->selection->clear_lines ();
5851 _editor->selection->clear_midi_notes ();
5853 _editor->commit_reversible_selection_op();
5856 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5861 _note[0] = _note[1] = 0;
5864 NoteCreateDrag::~NoteCreateDrag ()
5870 NoteCreateDrag::grid_frames (framepos_t t) const
5873 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5875 grid_beats = Evoral::Beats(1);
5878 return _region_view->region_beats_to_region_frames (grid_beats);
5882 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5884 Drag::start_grab (event, cursor);
5886 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5888 framepos_t pf = _drags->current_pointer_frame ();
5889 framecnt_t const g = grid_frames (pf);
5891 /* Hack so that we always snap to the note that we are over, instead of snapping
5892 to the next one if we're more than halfway through the one we're over.
5894 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5898 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5899 _note[1] = _note[0];
5901 MidiStreamView* sv = _region_view->midi_stream_view ();
5902 double const x = _editor->sample_to_pixel (_note[0]);
5903 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5905 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5906 _drag_rect->set_outline_all ();
5907 _drag_rect->set_outline_color (0xffffff99);
5908 _drag_rect->set_fill_color (0xffffff66);
5912 NoteCreateDrag::motion (GdkEvent* event, bool)
5914 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5915 double const x0 = _editor->sample_to_pixel (_note[0]);
5916 double const x1 = _editor->sample_to_pixel (_note[1]);
5917 _drag_rect->set_x0 (std::min(x0, x1));
5918 _drag_rect->set_x1 (std::max(x0, x1));
5922 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5924 if (!had_movement) {
5928 framepos_t const start = min (_note[0], _note[1]);
5929 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5931 framecnt_t const g = grid_frames (start);
5932 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5934 if (_editor->snap_mode() == SnapNormal && length < g) {
5938 Evoral::Beats length_beats = max (
5939 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5941 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5945 NoteCreateDrag::y_to_region (double y) const
5948 _region_view->get_canvas_group()->canvas_to_item (x, y);
5953 NoteCreateDrag::aborted (bool)
5958 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5963 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5967 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5969 Drag::start_grab (event, cursor);
5973 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5979 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5982 distance = _drags->current_pointer_x() - grab_x();
5983 len = ar->fade_in()->back()->when;
5985 distance = grab_x() - _drags->current_pointer_x();
5986 len = ar->fade_out()->back()->when;
5989 /* how long should it be ? */
5991 new_length = len + _editor->pixel_to_sample (distance);
5993 /* now check with the region that this is legal */
5995 new_length = ar->verify_xfade_bounds (new_length, start);
5998 arv->reset_fade_in_shape_width (ar, new_length);
6000 arv->reset_fade_out_shape_width (ar, new_length);
6005 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6011 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6014 distance = _drags->current_pointer_x() - grab_x();
6015 len = ar->fade_in()->back()->when;
6017 distance = grab_x() - _drags->current_pointer_x();
6018 len = ar->fade_out()->back()->when;
6021 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6023 _editor->begin_reversible_command ("xfade trim");
6024 ar->playlist()->clear_owned_changes ();
6027 ar->set_fade_in_length (new_length);
6029 ar->set_fade_out_length (new_length);
6032 /* Adjusting the xfade may affect other regions in the playlist, so we need
6033 to get undo Commands from the whole playlist rather than just the
6037 vector<Command*> cmds;
6038 ar->playlist()->rdiff (cmds);
6039 _editor->session()->add_commands (cmds);
6040 _editor->commit_reversible_command ();
6045 CrossfadeEdgeDrag::aborted (bool)
6048 // arv->redraw_start_xfade ();
6050 // arv->redraw_end_xfade ();
6054 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6055 : Drag (e, item, true)
6056 , line (new EditorCursor (*e))
6058 line->set_position (pos);
6062 RegionCutDrag::~RegionCutDrag ()
6068 RegionCutDrag::motion (GdkEvent*, bool)
6070 framepos_t where = _drags->current_pointer_frame();
6071 _editor->snap_to (where);
6073 line->set_position (where);
6077 RegionCutDrag::finished (GdkEvent*, bool)
6079 _editor->get_track_canvas()->canvas()->re_enter();
6081 framepos_t pos = _drags->current_pointer_frame();
6085 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6091 _editor->split_regions_at (pos, rs);
6095 RegionCutDrag::aborted (bool)