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 */
2385 /* select this note; if it is already selected, preserve the existing selection,
2386 otherwise make this note the only one selected.
2388 _editor->get_selection().clear_points();
2389 region->note_selected (cnote, cnote->selected ());
2393 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2395 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2397 _editor->begin_reversible_command (_("resize notes"));
2399 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2400 MidiRegionSelection::iterator next;
2403 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2405 mrv->begin_resizing (at_front);
2411 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2412 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2414 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2418 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2420 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2421 if (_editor->snap_mode () != SnapOff) {
2425 if (_editor->snap_mode () == SnapOff) {
2427 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2428 if (apply_snap_delta) {
2434 if (apply_snap_delta) {
2438 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2444 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2446 if (!movement_occurred) {
2450 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2451 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2452 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2454 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2457 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2459 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2460 if (_editor->snap_mode () != SnapOff) {
2464 if (_editor->snap_mode () == SnapOff) {
2466 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2467 if (apply_snap_delta) {
2473 if (apply_snap_delta) {
2477 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2481 _editor->commit_reversible_command ();
2485 NoteResizeDrag::aborted (bool)
2487 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2488 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2489 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2491 mrv->abort_resizing ();
2496 AVDraggingView::AVDraggingView (RegionView* v)
2499 initial_position = v->region()->position ();
2502 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2505 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2508 TrackViewList empty;
2510 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2511 std::list<RegionView*> views = rs.by_layer();
2514 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2515 RegionView* rv = (*i);
2516 if (!rv->region()->video_locked()) {
2519 if (rv->region()->locked()) {
2522 _views.push_back (AVDraggingView (rv));
2527 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2529 Drag::start_grab (event);
2530 if (_editor->session() == 0) {
2534 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2540 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2544 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2545 _max_backwards_drag = (
2546 ARDOUR_UI::instance()->video_timeline->get_duration()
2547 + ARDOUR_UI::instance()->video_timeline->get_offset()
2548 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2551 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2552 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2553 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2556 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2559 Timecode::Time timecode;
2560 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2561 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);
2562 show_verbose_cursor_text (buf);
2566 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2568 if (_editor->session() == 0) {
2571 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2575 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2579 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2580 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2582 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2583 dt = - _max_backwards_drag;
2586 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2587 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2589 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2590 RegionView* rv = i->view;
2591 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2594 rv->region()->clear_changes ();
2595 rv->region()->suspend_property_changes();
2597 rv->region()->set_position(i->initial_position + dt);
2598 rv->region_changed(ARDOUR::Properties::position);
2601 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2602 Timecode::Time timecode;
2603 Timecode::Time timediff;
2605 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2606 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2607 snprintf (buf, sizeof (buf),
2608 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2609 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2610 , _("Video Start:"),
2611 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2613 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2615 show_verbose_cursor_text (buf);
2619 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2621 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2628 if (!movement_occurred || ! _editor->session()) {
2632 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2634 _editor->begin_reversible_command (_("Move Video"));
2636 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2637 ARDOUR_UI::instance()->video_timeline->save_undo();
2638 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2639 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2641 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2642 i->view->drag_end();
2643 i->view->region()->resume_property_changes ();
2645 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2648 _editor->session()->maybe_update_session_range(
2649 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2650 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2654 _editor->commit_reversible_command ();
2658 VideoTimeLineDrag::aborted (bool)
2660 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2663 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2664 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2666 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2667 i->view->region()->resume_property_changes ();
2668 i->view->region()->set_position(i->initial_position);
2672 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2673 : RegionDrag (e, i, p, v)
2674 , _preserve_fade_anchor (preserve_fade_anchor)
2675 , _jump_position_when_done (false)
2677 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2681 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2684 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2685 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2687 if (tv && tv->is_track()) {
2688 speed = tv->track()->speed();
2691 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2692 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2693 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2695 framepos_t const pf = adjusted_current_frame (event);
2696 setup_snap_delta (region_start);
2698 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2699 /* Move the contents of the region around without changing the region bounds */
2700 _operation = ContentsTrim;
2701 Drag::start_grab (event, _editor->cursors()->trimmer);
2703 /* These will get overridden for a point trim.*/
2704 if (pf < (region_start + region_length/2)) {
2705 /* closer to front */
2706 _operation = StartTrim;
2707 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2708 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2710 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2714 _operation = EndTrim;
2715 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2716 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2718 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2722 /* jump trim disabled for now
2723 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2724 _jump_position_when_done = true;
2728 switch (_operation) {
2730 show_verbose_cursor_time (region_start);
2731 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2732 i->view->trim_front_starting ();
2736 show_verbose_cursor_duration (region_start, region_end);
2739 show_verbose_cursor_time (pf);
2743 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2744 i->view->region()->suspend_property_changes ();
2749 TrimDrag::motion (GdkEvent* event, bool first_move)
2751 RegionView* rv = _primary;
2754 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2755 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2756 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2757 frameoffset_t frame_delta = 0;
2759 if (tv && tv->is_track()) {
2760 speed = tv->track()->speed();
2762 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2763 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2769 switch (_operation) {
2771 trim_type = "Region start trim";
2774 trim_type = "Region end trim";
2777 trim_type = "Region content trim";
2784 _editor->begin_reversible_command (trim_type);
2786 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2787 RegionView* rv = i->view;
2788 rv->enable_display (false);
2789 rv->region()->playlist()->clear_owned_changes ();
2791 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2794 arv->temporarily_hide_envelope ();
2798 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2799 insert_result = _editor->motion_frozen_playlists.insert (pl);
2801 if (insert_result.second) {
2807 bool non_overlap_trim = false;
2809 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2810 non_overlap_trim = true;
2813 /* contstrain trim to fade length */
2814 if (_preserve_fade_anchor) {
2815 switch (_operation) {
2817 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2818 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2820 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2821 if (ar->locked()) continue;
2822 framecnt_t len = ar->fade_in()->back()->when;
2823 if (len < dt) dt = min(dt, len);
2827 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2828 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2830 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2831 if (ar->locked()) continue;
2832 framecnt_t len = ar->fade_out()->back()->when;
2833 if (len < -dt) dt = max(dt, -len);
2842 switch (_operation) {
2844 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2845 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2846 if (changed && _preserve_fade_anchor) {
2847 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2849 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2850 framecnt_t len = ar->fade_in()->back()->when;
2851 framecnt_t diff = ar->first_frame() - i->initial_position;
2852 framepos_t new_length = len - diff;
2853 i->anchored_fade_length = min (ar->length(), new_length);
2854 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2855 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2862 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2863 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2864 if (changed && _preserve_fade_anchor) {
2865 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2867 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2868 framecnt_t len = ar->fade_out()->back()->when;
2869 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2870 framepos_t new_length = len + diff;
2871 i->anchored_fade_length = min (ar->length(), new_length);
2872 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2873 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2881 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2883 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2884 i->view->move_contents (frame_delta);
2890 switch (_operation) {
2892 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2895 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2898 // show_verbose_cursor_time (frame_delta);
2904 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2906 if (movement_occurred) {
2907 motion (event, false);
2909 if (_operation == StartTrim) {
2910 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2912 /* This must happen before the region's StatefulDiffCommand is created, as it may
2913 `correct' (ahem) the region's _start from being negative to being zero. It
2914 needs to be zero in the undo record.
2916 i->view->trim_front_ending ();
2918 if (_preserve_fade_anchor) {
2919 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2921 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2922 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2923 ar->set_fade_in_length(i->anchored_fade_length);
2924 ar->set_fade_in_active(true);
2927 if (_jump_position_when_done) {
2928 i->view->region()->set_position (i->initial_position);
2931 } else if (_operation == EndTrim) {
2932 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2933 if (_preserve_fade_anchor) {
2934 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2936 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2937 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2938 ar->set_fade_out_length(i->anchored_fade_length);
2939 ar->set_fade_out_active(true);
2942 if (_jump_position_when_done) {
2943 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2948 if (!_views.empty()) {
2949 if (_operation == StartTrim) {
2950 _editor->maybe_locate_with_edit_preroll(
2951 _views.begin()->view->region()->position());
2953 if (_operation == EndTrim) {
2954 _editor->maybe_locate_with_edit_preroll(
2955 _views.begin()->view->region()->position() +
2956 _views.begin()->view->region()->length());
2960 if (!_editor->selection->selected (_primary)) {
2961 _primary->thaw_after_trim ();
2964 set<boost::shared_ptr<Playlist> > diffed_playlists;
2966 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2967 i->view->thaw_after_trim ();
2968 i->view->enable_display (true);
2970 /* Trimming one region may affect others on the playlist, so we need
2971 to get undo Commands from the whole playlist rather than just the
2972 region. Use diffed_playlists to make sure we don't diff a given
2973 playlist more than once.
2975 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2976 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2977 vector<Command*> cmds;
2979 _editor->session()->add_commands (cmds);
2980 diffed_playlists.insert (p);
2985 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2989 _editor->motion_frozen_playlists.clear ();
2990 _editor->commit_reversible_command();
2993 /* no mouse movement */
2994 _editor->point_trim (event, adjusted_current_frame (event));
2997 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2998 if (_operation == StartTrim) {
2999 i->view->trim_front_ending ();
3002 i->view->region()->resume_property_changes ();
3007 TrimDrag::aborted (bool movement_occurred)
3009 /* Our motion method is changing model state, so use the Undo system
3010 to cancel. Perhaps not ideal, as this will leave an Undo point
3011 behind which may be slightly odd from the user's point of view.
3016 if (movement_occurred) {
3020 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3021 i->view->region()->resume_property_changes ();
3026 TrimDrag::setup_pointer_frame_offset ()
3028 list<DraggingView>::iterator i = _views.begin ();
3029 while (i != _views.end() && i->view != _primary) {
3033 if (i == _views.end()) {
3037 switch (_operation) {
3039 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3042 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3049 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3053 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3054 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3059 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3061 Drag::start_grab (event, cursor);
3062 show_verbose_cursor_time (adjusted_current_frame(event));
3066 MeterMarkerDrag::setup_pointer_frame_offset ()
3068 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3072 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3074 if (!_marker->meter().movable()) {
3080 // create a dummy marker for visual representation of moving the
3081 // section, because whether its a copy or not, we're going to
3082 // leave or lose the original marker (leave if its a copy; lose if its
3083 // not, because we'll remove it from the map).
3085 MeterSection section (_marker->meter());
3087 if (!section.movable()) {
3092 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3094 _marker = new MeterMarker (
3096 *_editor->meter_group,
3097 UIConfiguration::instance().color ("meter marker"),
3099 *new MeterSection (_marker->meter())
3102 /* use the new marker for the grab */
3103 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3106 TempoMap& map (_editor->session()->tempo_map());
3107 /* get current state */
3108 before_state = &map.get_state();
3109 /* remove the section while we drag it */
3110 map.remove_meter (section, true);
3114 framepos_t const pf = adjusted_current_frame (event);
3116 _marker->set_position (pf);
3117 show_verbose_cursor_time (pf);
3121 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3123 if (!movement_occurred) {
3124 if (was_double_click()) {
3125 _editor->edit_meter_marker (*_marker);
3130 if (!_marker->meter().movable()) {
3134 motion (event, false);
3136 Timecode::BBT_Time when;
3138 TempoMap& map (_editor->session()->tempo_map());
3139 map.bbt_time (last_pointer_frame(), when);
3141 if (_copy == true) {
3142 _editor->begin_reversible_command (_("copy meter mark"));
3143 XMLNode &before = map.get_state();
3144 map.add_meter (_marker->meter(), when);
3145 XMLNode &after = map.get_state();
3146 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3147 _editor->commit_reversible_command ();
3150 _editor->begin_reversible_command (_("move meter mark"));
3152 /* we removed it before, so add it back now */
3154 map.add_meter (_marker->meter(), when);
3155 XMLNode &after = map.get_state();
3156 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3157 _editor->commit_reversible_command ();
3160 // delete the dummy marker we used for visual representation while moving.
3161 // a new visual marker will show up automatically.
3166 MeterMarkerDrag::aborted (bool moved)
3168 _marker->set_position (_marker->meter().frame ());
3171 TempoMap& map (_editor->session()->tempo_map());
3172 /* we removed it before, so add it back now */
3173 map.add_meter (_marker->meter(), _marker->meter().frame());
3174 // delete the dummy marker we used for visual representation while moving.
3175 // a new visual marker will show up automatically.
3180 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3184 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3186 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3191 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3193 Drag::start_grab (event, cursor);
3194 show_verbose_cursor_time (adjusted_current_frame (event));
3198 TempoMarkerDrag::setup_pointer_frame_offset ()
3200 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3204 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3206 if (!_marker->tempo().movable()) {
3212 // create a dummy marker for visual representation of moving the
3213 // section, because whether its a copy or not, we're going to
3214 // leave or lose the original marker (leave if its a copy; lose if its
3215 // not, because we'll remove it from the map).
3217 // create a dummy marker for visual representation of moving the copy.
3218 // The actual copying is not done before we reach the finish callback.
3221 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3223 TempoSection section (_marker->tempo());
3225 _marker = new TempoMarker (
3227 *_editor->tempo_group,
3228 UIConfiguration::instance().color ("tempo marker"),
3230 *new TempoSection (_marker->tempo())
3233 /* use the new marker for the grab */
3234 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3237 TempoMap& map (_editor->session()->tempo_map());
3238 /* get current state */
3239 before_state = &map.get_state();
3240 /* remove the section while we drag it */
3241 map.remove_tempo (section, true);
3245 framepos_t const pf = adjusted_current_frame (event);
3246 _marker->set_position (pf);
3247 show_verbose_cursor_time (pf);
3251 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3253 if (!movement_occurred) {
3254 if (was_double_click()) {
3255 _editor->edit_tempo_marker (*_marker);
3260 if (!_marker->tempo().movable()) {
3264 motion (event, false);
3266 TempoMap& map (_editor->session()->tempo_map());
3267 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3268 Timecode::BBT_Time when;
3270 map.bbt_time (beat_time, when);
3272 if (_copy == true) {
3273 _editor->begin_reversible_command (_("copy tempo mark"));
3274 XMLNode &before = map.get_state();
3275 map.add_tempo (_marker->tempo(), when);
3276 XMLNode &after = map.get_state();
3277 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3278 _editor->commit_reversible_command ();
3281 _editor->begin_reversible_command (_("move tempo mark"));
3282 /* we removed it before, so add it back now */
3283 map.add_tempo (_marker->tempo(), when);
3284 XMLNode &after = map.get_state();
3285 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3286 _editor->commit_reversible_command ();
3289 // delete the dummy marker we used for visual representation while moving.
3290 // a new visual marker will show up automatically.
3295 TempoMarkerDrag::aborted (bool moved)
3297 _marker->set_position (_marker->tempo().frame());
3299 TempoMap& map (_editor->session()->tempo_map());
3300 /* we removed it before, so add it back now */
3301 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3302 // delete the dummy marker we used for visual representation while moving.
3303 // a new visual marker will show up automatically.
3308 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3309 : Drag (e, &c.track_canvas_item(), false)
3313 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3316 /** Do all the things we do when dragging the playhead to make it look as though
3317 * we have located, without actually doing the locate (because that would cause
3318 * the diskstream buffers to be refilled, which is too slow).
3321 CursorDrag::fake_locate (framepos_t t)
3323 if (_editor->session () == 0) {
3327 _editor->playhead_cursor->set_position (t);
3329 Session* s = _editor->session ();
3330 if (s->timecode_transmission_suspended ()) {
3331 framepos_t const f = _editor->playhead_cursor->current_frame ();
3332 /* This is asynchronous so it will be sent "now"
3334 s->send_mmc_locate (f);
3335 /* These are synchronous and will be sent during the next
3338 s->queue_full_time_code ();
3339 s->queue_song_position_pointer ();
3342 show_verbose_cursor_time (t);
3343 _editor->UpdateAllTransportClocks (t);
3347 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3349 Drag::start_grab (event, c);
3350 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3352 _grab_zoom = _editor->samples_per_pixel;
3354 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3356 _editor->snap_to_with_modifier (where, event);
3358 _editor->_dragging_playhead = true;
3360 Session* s = _editor->session ();
3362 /* grab the track canvas item as well */
3364 _cursor.track_canvas_item().grab();
3367 if (_was_rolling && _stop) {
3371 if (s->is_auditioning()) {
3372 s->cancel_audition ();
3376 if (AudioEngine::instance()->connected()) {
3378 /* do this only if we're the engine is connected
3379 * because otherwise this request will never be
3380 * serviced and we'll busy wait forever. likewise,
3381 * notice if we are disconnected while waiting for the
3382 * request to be serviced.
3385 s->request_suspend_timecode_transmission ();
3386 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3387 /* twiddle our thumbs */
3392 fake_locate (where - snap_delta (event->button.state));
3396 CursorDrag::motion (GdkEvent* event, bool)
3398 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3399 _editor->snap_to_with_modifier (where, event);
3400 if (where != last_pointer_frame()) {
3401 fake_locate (where - snap_delta (event->button.state));
3406 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3408 _editor->_dragging_playhead = false;
3410 _cursor.track_canvas_item().ungrab();
3412 if (!movement_occurred && _stop) {
3416 motion (event, false);
3418 Session* s = _editor->session ();
3420 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3421 _editor->_pending_locate_request = true;
3422 s->request_resume_timecode_transmission ();
3427 CursorDrag::aborted (bool)
3429 _cursor.track_canvas_item().ungrab();
3431 if (_editor->_dragging_playhead) {
3432 _editor->session()->request_resume_timecode_transmission ();
3433 _editor->_dragging_playhead = false;
3436 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3439 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3440 : RegionDrag (e, i, p, v)
3442 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3446 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3448 Drag::start_grab (event, cursor);
3450 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3451 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3452 setup_snap_delta (r->position ());
3454 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3458 FadeInDrag::setup_pointer_frame_offset ()
3460 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3461 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3462 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3466 FadeInDrag::motion (GdkEvent* event, bool)
3468 framecnt_t fade_length;
3470 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3471 _editor->snap_to_with_modifier (pos, event);
3472 pos -= snap_delta (event->button.state);
3474 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3476 if (pos < (region->position() + 64)) {
3477 fade_length = 64; // this should be a minimum defined somewhere
3478 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3479 fade_length = region->length() - region->fade_out()->back()->when - 1;
3481 fade_length = pos - region->position();
3484 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3486 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3492 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3495 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3499 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3501 if (!movement_occurred) {
3505 framecnt_t fade_length;
3506 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3507 _editor->snap_to_with_modifier (pos, event);
3508 pos -= snap_delta (event->button.state);
3510 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3512 if (pos < (region->position() + 64)) {
3513 fade_length = 64; // this should be a minimum defined somewhere
3514 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3515 fade_length = region->length() - region->fade_out()->back()->when - 1;
3517 fade_length = pos - region->position();
3520 bool in_command = false;
3522 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3524 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3530 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3531 XMLNode &before = alist->get_state();
3533 tmp->audio_region()->set_fade_in_length (fade_length);
3534 tmp->audio_region()->set_fade_in_active (true);
3537 _editor->begin_reversible_command (_("change fade in length"));
3540 XMLNode &after = alist->get_state();
3541 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3545 _editor->commit_reversible_command ();
3550 FadeInDrag::aborted (bool)
3552 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3553 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3559 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3563 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3564 : RegionDrag (e, i, p, v)
3566 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3570 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3572 Drag::start_grab (event, cursor);
3574 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3575 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3576 setup_snap_delta (r->last_frame ());
3578 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3582 FadeOutDrag::setup_pointer_frame_offset ()
3584 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3585 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3586 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3590 FadeOutDrag::motion (GdkEvent* event, bool)
3592 framecnt_t fade_length;
3594 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3595 _editor->snap_to_with_modifier (pos, event);
3596 pos -= snap_delta (event->button.state);
3598 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3600 if (pos > (region->last_frame() - 64)) {
3601 fade_length = 64; // this should really be a minimum fade defined somewhere
3602 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3603 fade_length = region->length() - region->fade_in()->back()->when - 1;
3605 fade_length = region->last_frame() - pos;
3608 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3610 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3616 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3619 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3623 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3625 if (!movement_occurred) {
3629 framecnt_t fade_length;
3631 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3632 _editor->snap_to_with_modifier (pos, event);
3633 pos -= snap_delta (event->button.state);
3635 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3637 if (pos > (region->last_frame() - 64)) {
3638 fade_length = 64; // this should really be a minimum fade defined somewhere
3639 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3640 fade_length = region->length() - region->fade_in()->back()->when - 1;
3642 fade_length = region->last_frame() - pos;
3645 bool in_command = false;
3647 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3649 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3655 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3656 XMLNode &before = alist->get_state();
3658 tmp->audio_region()->set_fade_out_length (fade_length);
3659 tmp->audio_region()->set_fade_out_active (true);
3662 _editor->begin_reversible_command (_("change fade out length"));
3665 XMLNode &after = alist->get_state();
3666 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3670 _editor->commit_reversible_command ();
3675 FadeOutDrag::aborted (bool)
3677 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3678 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3684 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3688 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3691 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3693 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3696 _points.push_back (ArdourCanvas::Duple (0, 0));
3697 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3700 MarkerDrag::~MarkerDrag ()
3702 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3707 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3709 location = new Location (*l);
3710 markers.push_back (m);
3715 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3717 Drag::start_grab (event, cursor);
3721 Location *location = _editor->find_location_from_marker (_marker, is_start);
3722 _editor->_dragging_edit_point = true;
3724 update_item (location);
3726 // _drag_line->show();
3727 // _line->raise_to_top();
3730 show_verbose_cursor_time (location->start());
3732 show_verbose_cursor_time (location->end());
3735 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3738 case Selection::Toggle:
3739 /* we toggle on the button release */
3741 case Selection::Set:
3742 if (!_editor->selection->selected (_marker)) {
3743 _editor->selection->set (_marker);
3746 case Selection::Extend:
3748 Locations::LocationList ll;
3749 list<ArdourMarker*> to_add;
3751 _editor->selection->markers.range (s, e);
3752 s = min (_marker->position(), s);
3753 e = max (_marker->position(), e);
3756 if (e < max_framepos) {
3759 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3760 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3761 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3764 to_add.push_back (lm->start);
3767 to_add.push_back (lm->end);
3771 if (!to_add.empty()) {
3772 _editor->selection->add (to_add);
3776 case Selection::Add:
3777 _editor->selection->add (_marker);
3781 /* Set up copies for us to manipulate during the drag
3784 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3786 Location* l = _editor->find_location_from_marker (*i, is_start);
3793 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3795 /* range: check that the other end of the range isn't
3798 CopiedLocationInfo::iterator x;
3799 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3800 if (*(*x).location == *l) {
3804 if (x == _copied_locations.end()) {
3805 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3807 (*x).markers.push_back (*i);
3808 (*x).move_both = true;
3816 MarkerDrag::setup_pointer_frame_offset ()
3819 Location *location = _editor->find_location_from_marker (_marker, is_start);
3820 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3824 MarkerDrag::motion (GdkEvent* event, bool)
3826 framecnt_t f_delta = 0;
3828 bool move_both = false;
3829 Location *real_location;
3830 Location *copy_location = 0;
3832 framepos_t const newframe = adjusted_current_frame (event);
3833 framepos_t next = newframe;
3835 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
3839 CopiedLocationInfo::iterator x;
3841 /* find the marker we're dragging, and compute the delta */
3843 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3845 copy_location = (*x).location;
3847 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3849 /* this marker is represented by this
3850 * CopiedLocationMarkerInfo
3853 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3858 if (real_location->is_mark()) {
3859 f_delta = newframe - copy_location->start();
3863 switch (_marker->type()) {
3864 case ArdourMarker::SessionStart:
3865 case ArdourMarker::RangeStart:
3866 case ArdourMarker::LoopStart:
3867 case ArdourMarker::PunchIn:
3868 f_delta = newframe - copy_location->start();
3871 case ArdourMarker::SessionEnd:
3872 case ArdourMarker::RangeEnd:
3873 case ArdourMarker::LoopEnd:
3874 case ArdourMarker::PunchOut:
3875 f_delta = newframe - copy_location->end();
3878 /* what kind of marker is this ? */
3887 if (x == _copied_locations.end()) {
3888 /* hmm, impossible - we didn't find the dragged marker */
3892 /* now move them all */
3894 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3896 copy_location = x->location;
3898 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3902 if (real_location->locked()) {
3906 if (copy_location->is_mark()) {
3910 copy_location->set_start (copy_location->start() + f_delta);
3914 framepos_t new_start = copy_location->start() + f_delta;
3915 framepos_t new_end = copy_location->end() + f_delta;
3917 if (is_start) { // start-of-range marker
3919 if (move_both || (*x).move_both) {
3920 copy_location->set_start (new_start);
3921 copy_location->set_end (new_end);
3922 } else if (new_start < copy_location->end()) {
3923 copy_location->set_start (new_start);
3924 } else if (newframe > 0) {
3925 _editor->snap_to (next, RoundUpAlways, true);
3926 copy_location->set_end (next);
3927 copy_location->set_start (newframe);
3930 } else { // end marker
3932 if (move_both || (*x).move_both) {
3933 copy_location->set_end (new_end);
3934 copy_location->set_start (new_start);
3935 } else if (new_end > copy_location->start()) {
3936 copy_location->set_end (new_end);
3937 } else if (newframe > 0) {
3938 _editor->snap_to (next, RoundDownAlways, true);
3939 copy_location->set_start (next);
3940 copy_location->set_end (newframe);
3945 update_item (copy_location);
3947 /* now lookup the actual GUI items used to display this
3948 * location and move them to wherever the copy of the location
3949 * is now. This means that the logic in ARDOUR::Location is
3950 * still enforced, even though we are not (yet) modifying
3951 * the real Location itself.
3954 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3957 lm->set_position (copy_location->start(), copy_location->end());
3962 assert (!_copied_locations.empty());
3964 show_verbose_cursor_time (newframe);
3968 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3970 if (!movement_occurred) {
3972 if (was_double_click()) {
3973 _editor->rename_marker (_marker);
3977 /* just a click, do nothing but finish
3978 off the selection process
3981 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3984 case Selection::Set:
3985 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3986 _editor->selection->set (_marker);
3990 case Selection::Toggle:
3991 /* we toggle on the button release, click only */
3992 _editor->selection->toggle (_marker);
3995 case Selection::Extend:
3996 case Selection::Add:
4003 _editor->_dragging_edit_point = false;
4005 XMLNode &before = _editor->session()->locations()->get_state();
4006 bool in_command = false;
4008 MarkerSelection::iterator i;
4009 CopiedLocationInfo::iterator x;
4012 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4013 x != _copied_locations.end() && i != _editor->selection->markers.end();
4016 Location * location = _editor->find_location_from_marker (*i, is_start);
4020 if (location->locked()) {
4024 _editor->begin_reversible_command ( _("move marker") );
4027 if (location->is_mark()) {
4028 location->set_start (((*x).location)->start());
4030 location->set (((*x).location)->start(), ((*x).location)->end());
4036 XMLNode &after = _editor->session()->locations()->get_state();
4037 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4038 _editor->commit_reversible_command ();
4043 MarkerDrag::aborted (bool movement_occured)
4045 if (!movement_occured) {
4049 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4051 /* move all markers to their original location */
4054 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4057 Location * location = _editor->find_location_from_marker (*m, is_start);
4060 (*m)->set_position (is_start ? location->start() : location->end());
4067 MarkerDrag::update_item (Location*)
4072 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4074 _cumulative_x_drag (0)
4075 , _cumulative_y_drag (0)
4078 if (_zero_gain_fraction < 0.0) {
4079 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4082 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4084 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4090 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4092 Drag::start_grab (event, _editor->cursors()->fader);
4094 // start the grab at the center of the control point so
4095 // the point doesn't 'jump' to the mouse after the first drag
4096 _fixed_grab_x = _point->get_x();
4097 _fixed_grab_y = _point->get_y();
4099 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4100 setup_snap_delta (pos);
4102 float const fraction = 1 - (_point->get_y() / _point->line().height());
4103 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4105 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4107 if (!_point->can_slide ()) {
4108 _x_constrained = true;
4113 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4115 double dx = _drags->current_pointer_x() - last_pointer_x();
4116 double dy = current_pointer_y() - last_pointer_y();
4117 bool need_snap = true;
4119 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4125 /* coordinate in pixels relative to the start of the region (for region-based automation)
4126 or track (for track-based automation) */
4127 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4128 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4130 // calculate zero crossing point. back off by .01 to stay on the
4131 // positive side of zero
4132 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4134 if (_x_constrained) {
4137 if (_y_constrained) {
4141 _cumulative_x_drag = cx - _fixed_grab_x;
4142 _cumulative_y_drag = cy - _fixed_grab_y;
4144 // make sure we hit zero when passing through
4145 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4151 cy = min ((double) _point->line().height(), cy);
4153 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4155 if (!_x_constrained && need_snap) {
4156 _editor->snap_to_with_modifier (cx_frames, event);
4159 cx_frames -= snap_delta (event->button.state);
4160 cx_frames = min (cx_frames, _point->line().maximum_time());
4162 float const fraction = 1.0 - (cy / _point->line().height());
4165 _editor->begin_reversible_command (_("automation event move"));
4166 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
4169 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4171 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4175 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4177 if (!movement_occurred) {
4180 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4181 _editor->reset_point_selection ();
4185 motion (event, false);
4186 _point->line().end_drag (_pushing, _final_index);
4187 _editor->commit_reversible_command ();
4192 ControlPointDrag::aborted (bool)
4194 _point->line().reset ();
4198 ControlPointDrag::active (Editing::MouseMode m)
4200 if (m == Editing::MouseDraw) {
4201 /* always active in mouse draw */
4205 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4206 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4209 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4212 , _cumulative_y_drag (0)
4216 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4220 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4222 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4225 _item = &_line->grab_item ();
4227 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4228 origin, and ditto for y.
4231 double cx = event->button.x;
4232 double cy = event->button.y;
4234 _line->parent_group().canvas_to_item (cx, cy);
4236 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4238 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4239 /* no adjacent points */
4243 Drag::start_grab (event, _editor->cursors()->fader);
4245 /* store grab start in parent frame */
4250 double fraction = 1.0 - (cy / _line->height());
4252 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4256 LineDrag::motion (GdkEvent* event, bool first_move)
4258 double dy = current_pointer_y() - last_pointer_y();
4260 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4264 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4266 _cumulative_y_drag = cy - _fixed_grab_y;
4269 cy = min ((double) _line->height(), cy);
4271 double const fraction = 1.0 - (cy / _line->height());
4275 _editor->begin_reversible_command (_("automation range move"));
4276 _line->start_drag_line (_before, _after, fraction);
4279 /* we are ignoring x position for this drag, so we can just pass in anything */
4280 _line->drag_motion (0, fraction, true, false, ignored);
4282 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4286 LineDrag::finished (GdkEvent* event, bool movement_occured)
4288 if (movement_occured) {
4289 motion (event, false);
4290 _line->end_drag (false, 0);
4291 _editor->commit_reversible_command ();
4293 /* add a new control point on the line */
4295 AutomationTimeAxisView* atv;
4297 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4298 framepos_t where = _editor->canvas_event_sample (event, 0, 0);
4300 atv->add_automation_event (event, where, event->button.y, false);
4301 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4302 AudioRegionView* arv;
4304 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4305 arv->add_gain_point_event (arv->get_canvas_group (), event, false);
4312 LineDrag::aborted (bool)
4317 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4320 _cumulative_x_drag (0)
4322 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4326 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4328 Drag::start_grab (event);
4330 _line = reinterpret_cast<Line*> (_item);
4333 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4335 double cx = event->button.x;
4336 double cy = event->button.y;
4338 _item->parent()->canvas_to_item (cx, cy);
4340 /* store grab start in parent frame */
4341 _region_view_grab_x = cx;
4343 _before = *(float*) _item->get_data ("position");
4345 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4347 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4351 FeatureLineDrag::motion (GdkEvent*, bool)
4353 double dx = _drags->current_pointer_x() - last_pointer_x();
4355 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4357 _cumulative_x_drag += dx;
4359 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4368 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4370 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4372 float *pos = new float;
4375 _line->set_data ("position", pos);
4381 FeatureLineDrag::finished (GdkEvent*, bool)
4383 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4384 _arv->update_transient(_before, _before);
4388 FeatureLineDrag::aborted (bool)
4393 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4395 , _vertical_only (false)
4397 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4401 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4403 Drag::start_grab (event);
4404 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4408 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4415 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4417 framepos_t grab = grab_frame ();
4418 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4419 _editor->snap_to_with_modifier (grab, event);
4421 grab = raw_grab_frame ();
4424 /* base start and end on initial click position */
4434 if (current_pointer_y() < grab_y()) {
4435 y1 = current_pointer_y();
4438 y2 = current_pointer_y();
4442 if (start != end || y1 != y2) {
4444 double x1 = _editor->sample_to_pixel (start);
4445 double x2 = _editor->sample_to_pixel (end);
4446 const double min_dimension = 2.0;
4448 if (_vertical_only) {
4449 /* fixed 10 pixel width */
4453 x2 = min (x1 - min_dimension, x2);
4455 x2 = max (x1 + min_dimension, x2);
4460 y2 = min (y1 - min_dimension, y2);
4462 y2 = max (y1 + min_dimension, y2);
4465 /* translate rect into item space and set */
4467 ArdourCanvas::Rect r (x1, y1, x2, y2);
4469 /* this drag is a _trackview_only == true drag, so the y1 and
4470 * y2 (computed using current_pointer_y() and grab_y()) will be
4471 * relative to the top of the trackview group). The
4472 * rubberband rect has the same parent/scroll offset as the
4473 * the trackview group, so we can use the "r" rect directly
4474 * to set the shape of the rubberband.
4477 _editor->rubberband_rect->set (r);
4478 _editor->rubberband_rect->show();
4479 _editor->rubberband_rect->raise_to_top();
4481 show_verbose_cursor_time (pf);
4483 do_select_things (event, true);
4488 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4492 framepos_t grab = grab_frame ();
4493 framepos_t lpf = last_pointer_frame ();
4495 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4496 grab = raw_grab_frame ();
4497 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4511 if (current_pointer_y() < grab_y()) {
4512 y1 = current_pointer_y();
4515 y2 = current_pointer_y();
4519 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4523 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4525 if (movement_occurred) {
4527 motion (event, false);
4528 do_select_things (event, false);
4534 bool do_deselect = true;
4535 MidiTimeAxisView* mtv;
4537 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4539 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4540 /* nothing selected */
4541 add_midi_region (mtv);
4542 do_deselect = false;
4546 /* do not deselect if Primary or Tertiary (toggle-select or
4547 * extend-select are pressed.
4550 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4551 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4558 _editor->rubberband_rect->hide();
4562 RubberbandSelectDrag::aborted (bool)
4564 _editor->rubberband_rect->hide ();
4567 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4568 : RegionDrag (e, i, p, v)
4570 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4574 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4576 Drag::start_grab (event, cursor);
4578 _editor->get_selection().add (_primary);
4580 framepos_t where = _primary->region()->position();
4581 setup_snap_delta (where);
4583 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4587 TimeFXDrag::motion (GdkEvent* event, bool)
4589 RegionView* rv = _primary;
4590 StreamView* cv = rv->get_time_axis_view().view ();
4592 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4593 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4594 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4595 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4596 _editor->snap_to_with_modifier (pf, event);
4597 pf -= snap_delta (event->button.state);
4599 if (pf > rv->region()->position()) {
4600 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4603 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4607 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4609 _primary->get_time_axis_view().hide_timestretch ();
4611 if (!movement_occurred) {
4615 if (last_pointer_frame() < _primary->region()->position()) {
4616 /* backwards drag of the left edge - not usable */
4620 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4622 float percentage = (double) newlen / (double) _primary->region()->length();
4624 #ifndef USE_RUBBERBAND
4625 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4626 if (_primary->region()->data_type() == DataType::AUDIO) {
4627 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4631 if (!_editor->get_selection().regions.empty()) {
4632 /* primary will already be included in the selection, and edit
4633 group shared editing will propagate selection across
4634 equivalent regions, so just use the current region
4638 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4639 error << _("An error occurred while executing time stretch operation") << endmsg;
4645 TimeFXDrag::aborted (bool)
4647 _primary->get_time_axis_view().hide_timestretch ();
4650 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4653 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4657 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4659 Drag::start_grab (event);
4663 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4665 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4669 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4671 if (movement_occurred && _editor->session()) {
4672 /* make sure we stop */
4673 _editor->session()->request_transport_speed (0.0);
4678 ScrubDrag::aborted (bool)
4683 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4687 , _time_selection_at_start (!_editor->get_selection().time.empty())
4689 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4691 if (_time_selection_at_start) {
4692 start_at_start = _editor->get_selection().time.start();
4693 end_at_start = _editor->get_selection().time.end_frame();
4698 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4700 if (_editor->session() == 0) {
4704 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4706 switch (_operation) {
4707 case CreateSelection:
4708 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4713 cursor = _editor->cursors()->selector;
4714 Drag::start_grab (event, cursor);
4717 case SelectionStartTrim:
4718 if (_editor->clicked_axisview) {
4719 _editor->clicked_axisview->order_selection_trims (_item, true);
4721 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4724 case SelectionEndTrim:
4725 if (_editor->clicked_axisview) {
4726 _editor->clicked_axisview->order_selection_trims (_item, false);
4728 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4732 Drag::start_grab (event, cursor);
4735 case SelectionExtend:
4736 Drag::start_grab (event, cursor);
4740 if (_operation == SelectionMove) {
4741 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4743 show_verbose_cursor_time (adjusted_current_frame (event));
4748 SelectionDrag::setup_pointer_frame_offset ()
4750 switch (_operation) {
4751 case CreateSelection:
4752 _pointer_frame_offset = 0;
4755 case SelectionStartTrim:
4757 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4760 case SelectionEndTrim:
4761 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4764 case SelectionExtend:
4770 SelectionDrag::motion (GdkEvent* event, bool first_move)
4772 framepos_t start = 0;
4774 framecnt_t length = 0;
4775 framecnt_t distance = 0;
4777 framepos_t const pending_position = adjusted_current_frame (event);
4779 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4783 switch (_operation) {
4784 case CreateSelection:
4786 framepos_t grab = grab_frame ();
4789 grab = adjusted_current_frame (event, false);
4790 if (grab < pending_position) {
4791 _editor->snap_to (grab, RoundDownMaybe);
4793 _editor->snap_to (grab, RoundUpMaybe);
4797 if (pending_position < grab) {
4798 start = pending_position;
4801 end = pending_position;
4805 /* first drag: Either add to the selection
4806 or create a new selection
4813 /* adding to the selection */
4814 _editor->set_selected_track_as_side_effect (Selection::Add);
4815 _editor->clicked_selection = _editor->selection->add (start, end);
4822 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4823 _editor->set_selected_track_as_side_effect (Selection::Set);
4826 _editor->clicked_selection = _editor->selection->set (start, end);
4830 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4831 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4832 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4834 _editor->selection->add (atest);
4838 /* select all tracks within the rectangle that we've marked out so far */
4839 TrackViewList new_selection;
4840 TrackViewList& all_tracks (_editor->track_views);
4842 ArdourCanvas::Coord const top = grab_y();
4843 ArdourCanvas::Coord const bottom = current_pointer_y();
4845 if (top >= 0 && bottom >= 0) {
4847 //first, find the tracks that are covered in the y range selection
4848 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4849 if ((*i)->covered_by_y_range (top, bottom)) {
4850 new_selection.push_back (*i);
4854 //now find any tracks that are GROUPED with the tracks we selected
4855 TrackViewList grouped_add = new_selection;
4856 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4857 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4858 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4859 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4860 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4861 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4862 grouped_add.push_back (*j);
4867 //now compare our list with the current selection, and add or remove as necessary
4868 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4869 TrackViewList tracks_to_add;
4870 TrackViewList tracks_to_remove;
4871 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4872 if ( !_editor->selection->tracks.contains ( *i ) )
4873 tracks_to_add.push_back ( *i );
4874 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4875 if ( !grouped_add.contains ( *i ) )
4876 tracks_to_remove.push_back ( *i );
4877 _editor->selection->add(tracks_to_add);
4878 _editor->selection->remove(tracks_to_remove);
4884 case SelectionStartTrim:
4886 end = _editor->selection->time[_editor->clicked_selection].end;
4888 if (pending_position > end) {
4891 start = pending_position;
4895 case SelectionEndTrim:
4897 start = _editor->selection->time[_editor->clicked_selection].start;
4899 if (pending_position < start) {
4902 end = pending_position;
4909 start = _editor->selection->time[_editor->clicked_selection].start;
4910 end = _editor->selection->time[_editor->clicked_selection].end;
4912 length = end - start;
4913 distance = pending_position - start;
4914 start = pending_position;
4915 _editor->snap_to (start);
4917 end = start + length;
4921 case SelectionExtend:
4926 switch (_operation) {
4928 if (_time_selection_at_start) {
4929 _editor->selection->move_time (distance);
4933 _editor->selection->replace (_editor->clicked_selection, start, end);
4937 if (_operation == SelectionMove) {
4938 show_verbose_cursor_time(start);
4940 show_verbose_cursor_time(pending_position);
4945 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4947 Session* s = _editor->session();
4949 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4950 if (movement_occurred) {
4951 motion (event, false);
4952 /* XXX this is not object-oriented programming at all. ick */
4953 if (_editor->selection->time.consolidate()) {
4954 _editor->selection->TimeChanged ();
4957 /* XXX what if its a music time selection? */
4959 if (s->get_play_range() && s->transport_rolling()) {
4960 s->request_play_range (&_editor->selection->time, true);
4961 } else if (!s->config.get_external_sync()) {
4962 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
4963 if (_operation == SelectionEndTrim)
4964 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4966 s->request_locate (_editor->get_selection().time.start());
4970 if (_editor->get_selection().time.length() != 0) {
4971 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
4973 s->clear_range_selection ();
4978 /* just a click, no pointer movement.
4981 if (_operation == SelectionExtend) {
4982 if (_time_selection_at_start) {
4983 framepos_t pos = adjusted_current_frame (event, false);
4984 framepos_t start = min (pos, start_at_start);
4985 framepos_t end = max (pos, end_at_start);
4986 _editor->selection->set (start, end);
4989 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4990 if (_editor->clicked_selection) {
4991 _editor->selection->remove (_editor->clicked_selection);
4994 if (!_editor->clicked_selection) {
4995 _editor->selection->clear_time();
5000 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5001 _editor->selection->set (_editor->clicked_axisview);
5004 if (s && s->get_play_range () && s->transport_rolling()) {
5005 s->request_stop (false, false);
5010 _editor->stop_canvas_autoscroll ();
5011 _editor->clicked_selection = 0;
5012 _editor->commit_reversible_selection_op ();
5016 SelectionDrag::aborted (bool)
5021 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5022 : Drag (e, i, false),
5026 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5028 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5029 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5030 physical_screen_height (_editor->get_window())));
5031 _drag_rect->hide ();
5033 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5034 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5037 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5039 /* normal canvas items will be cleaned up when their parent group is deleted. But
5040 this item is created as the child of a long-lived parent group, and so we
5041 need to explicitly delete it.
5047 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5049 if (_editor->session() == 0) {
5053 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5055 if (!_editor->temp_location) {
5056 _editor->temp_location = new Location (*_editor->session());
5059 switch (_operation) {
5060 case CreateSkipMarker:
5061 case CreateRangeMarker:
5062 case CreateTransportMarker:
5063 case CreateCDMarker:
5065 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5070 cursor = _editor->cursors()->selector;
5074 Drag::start_grab (event, cursor);
5076 show_verbose_cursor_time (adjusted_current_frame (event));
5080 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5082 framepos_t start = 0;
5084 ArdourCanvas::Rectangle *crect;
5086 switch (_operation) {
5087 case CreateSkipMarker:
5088 crect = _editor->range_bar_drag_rect;
5090 case CreateRangeMarker:
5091 crect = _editor->range_bar_drag_rect;
5093 case CreateTransportMarker:
5094 crect = _editor->transport_bar_drag_rect;
5096 case CreateCDMarker:
5097 crect = _editor->cd_marker_bar_drag_rect;
5100 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5105 framepos_t const pf = adjusted_current_frame (event);
5107 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5108 framepos_t grab = grab_frame ();
5109 _editor->snap_to (grab);
5111 if (pf < grab_frame()) {
5119 /* first drag: Either add to the selection
5120 or create a new selection.
5125 _editor->temp_location->set (start, end);
5129 update_item (_editor->temp_location);
5131 //_drag_rect->raise_to_top();
5137 _editor->temp_location->set (start, end);
5139 double x1 = _editor->sample_to_pixel (start);
5140 double x2 = _editor->sample_to_pixel (end);
5144 update_item (_editor->temp_location);
5147 show_verbose_cursor_time (pf);
5152 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5154 Location * newloc = 0;
5158 if (movement_occurred) {
5159 motion (event, false);
5162 switch (_operation) {
5163 case CreateSkipMarker:
5164 case CreateRangeMarker:
5165 case CreateCDMarker:
5167 XMLNode &before = _editor->session()->locations()->get_state();
5168 if (_operation == CreateSkipMarker) {
5169 _editor->begin_reversible_command (_("new skip marker"));
5170 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5171 flags = Location::IsRangeMarker | Location::IsSkip;
5172 _editor->range_bar_drag_rect->hide();
5173 } else if (_operation == CreateCDMarker) {
5174 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5175 _editor->begin_reversible_command (_("new CD marker"));
5176 flags = Location::IsRangeMarker | Location::IsCDMarker;
5177 _editor->cd_marker_bar_drag_rect->hide();
5179 _editor->begin_reversible_command (_("new skip marker"));
5180 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5181 flags = Location::IsRangeMarker;
5182 _editor->range_bar_drag_rect->hide();
5184 newloc = new Location (
5185 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5188 _editor->session()->locations()->add (newloc, true);
5189 XMLNode &after = _editor->session()->locations()->get_state();
5190 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5191 _editor->commit_reversible_command ();
5195 case CreateTransportMarker:
5196 // popup menu to pick loop or punch
5197 _editor->new_transport_marker_context_menu (&event->button, _item);
5203 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5205 if (_operation == CreateTransportMarker) {
5207 /* didn't drag, so just locate */
5209 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5211 } else if (_operation == CreateCDMarker) {
5213 /* didn't drag, but mark is already created so do
5216 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5221 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5223 if (end == max_framepos) {
5224 end = _editor->session()->current_end_frame ();
5227 if (start == max_framepos) {
5228 start = _editor->session()->current_start_frame ();
5231 switch (_editor->mouse_mode) {
5233 /* find the two markers on either side and then make the selection from it */
5234 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5238 /* find the two markers on either side of the click and make the range out of it */
5239 _editor->selection->set (start, end);
5248 _editor->stop_canvas_autoscroll ();
5252 RangeMarkerBarDrag::aborted (bool movement_occured)
5254 if (movement_occured) {
5255 _drag_rect->hide ();
5260 RangeMarkerBarDrag::update_item (Location* location)
5262 double const x1 = _editor->sample_to_pixel (location->start());
5263 double const x2 = _editor->sample_to_pixel (location->end());
5265 _drag_rect->set_x0 (x1);
5266 _drag_rect->set_x1 (x2);
5269 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5271 , _cumulative_dx (0)
5272 , _cumulative_dy (0)
5274 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5276 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5278 _region = &_primary->region_view ();
5279 _note_height = _region->midi_stream_view()->note_height ();
5283 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5285 Drag::start_grab (event);
5286 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5288 if (!(_was_selected = _primary->selected())) {
5290 /* tertiary-click means extend selection - we'll do that on button release,
5291 so don't add it here, because otherwise we make it hard to figure
5292 out the "extend-to" range.
5295 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5298 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5301 _region->note_selected (_primary, true);
5303 _editor->get_selection().clear_points();
5304 _region->unique_select (_primary);
5310 /** @return Current total drag x change in frames */
5312 NoteDrag::total_dx (const guint state) const
5315 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5317 /* primary note time */
5318 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5320 /* new time of the primary note in session frames */
5321 frameoffset_t st = n + dx + snap_delta (state);
5323 framepos_t const rp = _region->region()->position ();
5325 /* prevent the note being dragged earlier than the region's position */
5328 /* possibly snap and return corresponding delta */
5332 if (ArdourKeyboard::indicates_snap (state)) {
5333 if (_editor->snap_mode () != SnapOff) {
5337 if (_editor->snap_mode () == SnapOff) {
5339 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5340 if (ArdourKeyboard::indicates_snap_delta (state)) {
5348 ret = _region->snap_frame_to_frame (st - rp) + rp - n - snap_delta (state);
5350 ret = st - n - snap_delta (state);
5355 /** @return Current total drag y change in note number */
5357 NoteDrag::total_dy () const
5359 MidiStreamView* msv = _region->midi_stream_view ();
5360 double const y = _region->midi_view()->y_position ();
5361 /* new current note */
5362 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5364 n = max (msv->lowest_note(), n);
5365 n = min (msv->highest_note(), n);
5366 /* and work out delta */
5367 return n - msv->y_to_note (grab_y() - y);
5371 NoteDrag::motion (GdkEvent * event, bool)
5373 /* Total change in x and y since the start of the drag */
5374 frameoffset_t const dx = total_dx (event->button.state);
5375 int8_t const dy = total_dy ();
5377 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5378 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5379 double const tdy = -dy * _note_height - _cumulative_dy;
5382 _cumulative_dx += tdx;
5383 _cumulative_dy += tdy;
5385 int8_t note_delta = total_dy();
5387 _region->move_selection (tdx, tdy, note_delta);
5389 /* the new note value may be the same as the old one, but we
5390 * don't know what that means because the selection may have
5391 * involved more than one note and we might be doing something
5392 * odd with them. so show the note value anyway, always.
5396 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5398 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5399 (int) floor ((double)new_note));
5401 show_verbose_cursor_text (buf);
5406 NoteDrag::finished (GdkEvent* ev, bool moved)
5409 /* no motion - select note */
5411 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5412 _editor->current_mouse_mode() == Editing::MouseDraw) {
5414 bool changed = false;
5416 if (_was_selected) {
5417 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5419 _region->note_deselected (_primary);
5422 _editor->get_selection().clear_points();
5423 _region->unique_select (_primary);
5427 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5428 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5430 if (!extend && !add && _region->selection_size() > 1) {
5431 _editor->get_selection().clear_points();
5432 _region->unique_select (_primary);
5434 } else if (extend) {
5435 _region->note_selected (_primary, true, true);
5438 /* it was added during button press */
5445 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5446 _editor->commit_reversible_selection_op();
5450 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5455 NoteDrag::aborted (bool)
5460 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5461 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5462 : Drag (editor, atv->base_item ())
5464 , _y_origin (atv->y_position())
5465 , _nothing_to_drag (false)
5467 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5468 setup (atv->lines ());
5471 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5472 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5473 : Drag (editor, rv->get_canvas_group ())
5475 , _y_origin (rv->get_time_axis_view().y_position())
5476 , _nothing_to_drag (false)
5479 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5481 list<boost::shared_ptr<AutomationLine> > lines;
5483 AudioRegionView* audio_view;
5484 AutomationRegionView* automation_view;
5485 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5486 lines.push_back (audio_view->get_gain_line ());
5487 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5488 lines.push_back (automation_view->line ());
5491 error << _("Automation range drag created for invalid region type") << endmsg;
5497 /** @param lines AutomationLines to drag.
5498 * @param offset Offset from the session start to the points in the AutomationLines.
5501 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5503 /* find the lines that overlap the ranges being dragged */
5504 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5505 while (i != lines.end ()) {
5506 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5509 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5511 /* check this range against all the AudioRanges that we are using */
5512 list<AudioRange>::const_iterator k = _ranges.begin ();
5513 while (k != _ranges.end()) {
5514 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5520 /* add it to our list if it overlaps at all */
5521 if (k != _ranges.end()) {
5526 _lines.push_back (n);
5532 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5536 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5538 return 1.0 - ((global_y - _y_origin) / line->height());
5542 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5544 const double v = list->eval(x);
5545 return _integral ? rint(v) : v;
5549 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5551 Drag::start_grab (event, cursor);
5553 /* Get line states before we start changing things */
5554 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5555 i->state = &i->line->get_state ();
5556 i->original_fraction = y_fraction (i->line, current_pointer_y());
5559 if (_ranges.empty()) {
5561 /* No selected time ranges: drag all points */
5562 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5563 uint32_t const N = i->line->npoints ();
5564 for (uint32_t j = 0; j < N; ++j) {
5565 i->points.push_back (i->line->nth (j));
5571 if (_nothing_to_drag) {
5577 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5579 if (_nothing_to_drag && !first_move) {
5584 _editor->begin_reversible_command (_("automation range move"));
5586 if (!_ranges.empty()) {
5588 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5590 framecnt_t const half = (i->start + i->end) / 2;
5592 /* find the line that this audio range starts in */
5593 list<Line>::iterator j = _lines.begin();
5594 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
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 into it;
5602 64 samples length plucked out of thin air.
5605 framepos_t a = i->start + 64;
5610 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5611 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5613 XMLNode &before = the_list->get_state();
5614 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5615 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5617 if (add_p || add_q) {
5618 _editor->session()->add_command (
5619 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5623 /* same thing for the end */
5626 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5630 if (j != _lines.end()) {
5631 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5633 /* j is the line that this audio range starts in; fade out of it;
5634 64 samples length plucked out of thin air.
5637 framepos_t b = i->end - 64;
5642 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5643 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5645 XMLNode &before = the_list->get_state();
5646 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5647 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5649 if (add_p || add_q) {
5650 _editor->session()->add_command (
5651 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5656 _nothing_to_drag = true;
5658 /* Find all the points that should be dragged and put them in the relevant
5659 points lists in the Line structs.
5662 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5664 uint32_t const N = i->line->npoints ();
5665 for (uint32_t j = 0; j < N; ++j) {
5667 /* here's a control point on this line */
5668 ControlPoint* p = i->line->nth (j);
5669 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5671 /* see if it's inside a range */
5672 list<AudioRange>::const_iterator k = _ranges.begin ();
5673 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5677 if (k != _ranges.end()) {
5678 /* dragging this point */
5679 _nothing_to_drag = false;
5680 i->points.push_back (p);
5686 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5687 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5691 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5692 float const f = y_fraction (l->line, current_pointer_y());
5693 /* we are ignoring x position for this drag, so we can just pass in anything */
5695 l->line->drag_motion (0, f, true, false, ignored);
5696 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5701 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5703 if (_nothing_to_drag || !motion_occurred) {
5707 motion (event, false);
5708 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5709 i->line->end_drag (false, 0);
5712 _editor->commit_reversible_command ();
5716 AutomationRangeDrag::aborted (bool)
5718 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5723 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5725 , initial_time_axis_view (itav)
5727 /* note that time_axis_view may be null if the regionview was created
5728 * as part of a copy operation.
5730 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5731 layer = v->region()->layer ();
5732 initial_y = v->get_canvas_group()->position().y;
5733 initial_playlist = v->region()->playlist ();
5734 initial_position = v->region()->position ();
5735 initial_end = v->region()->position () + v->region()->length ();
5738 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5739 : Drag (e, i->canvas_item ())
5742 , _cumulative_dx (0)
5744 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5745 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5750 PatchChangeDrag::motion (GdkEvent* ev, bool)
5752 framepos_t f = adjusted_current_frame (ev);
5753 boost::shared_ptr<Region> r = _region_view->region ();
5754 f = max (f, r->position ());
5755 f = min (f, r->last_frame ());
5757 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5758 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5759 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5760 _cumulative_dx = dxu;
5764 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5766 if (!movement_occurred) {
5770 boost::shared_ptr<Region> r (_region_view->region ());
5771 framepos_t f = adjusted_current_frame (ev);
5772 f = max (f, r->position ());
5773 f = min (f, r->last_frame ());
5775 _region_view->move_patch_change (
5777 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5782 PatchChangeDrag::aborted (bool)
5784 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5788 PatchChangeDrag::setup_pointer_frame_offset ()
5790 boost::shared_ptr<Region> region = _region_view->region ();
5791 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5794 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5795 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5802 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5804 _region_view->update_drag_selection (
5806 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5810 MidiRubberbandSelectDrag::deselect_things ()
5815 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5816 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5819 _vertical_only = true;
5823 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5825 double const y = _region_view->midi_view()->y_position ();
5827 y1 = max (0.0, y1 - y);
5828 y2 = max (0.0, y2 - y);
5830 _region_view->update_vertical_drag_selection (
5833 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5838 MidiVerticalSelectDrag::deselect_things ()
5843 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5844 : RubberbandSelectDrag (e, i)
5850 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5852 if (drag_in_progress) {
5853 /* We just want to select things at the end of the drag, not during it */
5857 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5859 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5861 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5863 _editor->commit_reversible_selection_op ();
5867 EditorRubberbandSelectDrag::deselect_things ()
5869 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5871 _editor->selection->clear_tracks();
5872 _editor->selection->clear_regions();
5873 _editor->selection->clear_points ();
5874 _editor->selection->clear_lines ();
5875 _editor->selection->clear_midi_notes ();
5877 _editor->commit_reversible_selection_op();
5880 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5885 _note[0] = _note[1] = 0;
5888 NoteCreateDrag::~NoteCreateDrag ()
5894 NoteCreateDrag::grid_frames (framepos_t t) const
5897 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5899 grid_beats = Evoral::Beats(1);
5902 return _region_view->region_beats_to_region_frames (grid_beats);
5906 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5908 Drag::start_grab (event, cursor);
5910 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5912 framepos_t pf = _drags->current_pointer_frame ();
5913 framecnt_t const g = grid_frames (pf);
5915 /* Hack so that we always snap to the note that we are over, instead of snapping
5916 to the next one if we're more than halfway through the one we're over.
5918 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5922 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5923 _note[1] = _note[0];
5925 MidiStreamView* sv = _region_view->midi_stream_view ();
5926 double const x = _editor->sample_to_pixel (_note[0]);
5927 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5929 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5930 _drag_rect->set_outline_all ();
5931 _drag_rect->set_outline_color (0xffffff99);
5932 _drag_rect->set_fill_color (0xffffff66);
5936 NoteCreateDrag::motion (GdkEvent* event, bool)
5938 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5939 double const x0 = _editor->sample_to_pixel (_note[0]);
5940 double const x1 = _editor->sample_to_pixel (_note[1]);
5941 _drag_rect->set_x0 (std::min(x0, x1));
5942 _drag_rect->set_x1 (std::max(x0, x1));
5946 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5948 if (!had_movement) {
5952 framepos_t const start = min (_note[0], _note[1]);
5953 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5955 framecnt_t const g = grid_frames (start);
5956 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5958 if (_editor->snap_mode() == SnapNormal && length < g) {
5962 Evoral::Beats length_beats = max (
5963 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5965 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5969 NoteCreateDrag::y_to_region (double y) const
5972 _region_view->get_canvas_group()->canvas_to_item (x, y);
5977 NoteCreateDrag::aborted (bool)
5982 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5987 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5991 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5993 Drag::start_grab (event, cursor);
5997 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6003 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6006 distance = _drags->current_pointer_x() - grab_x();
6007 len = ar->fade_in()->back()->when;
6009 distance = grab_x() - _drags->current_pointer_x();
6010 len = ar->fade_out()->back()->when;
6013 /* how long should it be ? */
6015 new_length = len + _editor->pixel_to_sample (distance);
6017 /* now check with the region that this is legal */
6019 new_length = ar->verify_xfade_bounds (new_length, start);
6022 arv->reset_fade_in_shape_width (ar, new_length);
6024 arv->reset_fade_out_shape_width (ar, new_length);
6029 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6035 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6038 distance = _drags->current_pointer_x() - grab_x();
6039 len = ar->fade_in()->back()->when;
6041 distance = grab_x() - _drags->current_pointer_x();
6042 len = ar->fade_out()->back()->when;
6045 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6047 _editor->begin_reversible_command ("xfade trim");
6048 ar->playlist()->clear_owned_changes ();
6051 ar->set_fade_in_length (new_length);
6053 ar->set_fade_out_length (new_length);
6056 /* Adjusting the xfade may affect other regions in the playlist, so we need
6057 to get undo Commands from the whole playlist rather than just the
6061 vector<Command*> cmds;
6062 ar->playlist()->rdiff (cmds);
6063 _editor->session()->add_commands (cmds);
6064 _editor->commit_reversible_command ();
6069 CrossfadeEdgeDrag::aborted (bool)
6072 // arv->redraw_start_xfade ();
6074 // arv->redraw_end_xfade ();
6078 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6079 : Drag (e, item, true)
6080 , line (new EditorCursor (*e))
6082 line->set_position (pos);
6086 RegionCutDrag::~RegionCutDrag ()
6092 RegionCutDrag::motion (GdkEvent*, bool)
6094 framepos_t where = _drags->current_pointer_frame();
6095 _editor->snap_to (where);
6097 line->set_position (where);
6101 RegionCutDrag::finished (GdkEvent*, bool)
6103 _editor->get_track_canvas()->canvas()->re_enter();
6105 framepos_t pos = _drags->current_pointer_frame();
6109 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6115 _editor->split_regions_at (pos, rs);
6119 RegionCutDrag::aborted (bool)