2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
49 #include "audio_region_view.h"
50 #include "automation_region_view.h"
51 #include "midi_region_view.h"
52 #include "ardour_ui.h"
53 #include "gui_thread.h"
54 #include "control_point.h"
55 #include "region_gain_line.h"
56 #include "editor_drag.h"
57 #include "audio_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "selection.h"
60 #include "midi_selection.h"
61 #include "automation_time_axis.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "verbose_cursor.h"
70 using namespace ARDOUR;
73 using namespace Gtkmm2ext;
74 using namespace Editing;
75 using namespace ArdourCanvas;
77 using Gtkmm2ext::Keyboard;
79 double ControlPointDrag::_zero_gain_fraction = -1.0;
81 DragManager::DragManager (Editor* e)
84 , _current_pointer_frame (0)
88 DragManager::~DragManager ()
93 /** Call abort for each active drag */
99 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
104 if (!_drags.empty ()) {
105 _editor->set_follow_playhead (_old_follow_playhead, false);
109 _editor->abort_reversible_command();
115 DragManager::add (Drag* d)
117 d->set_manager (this);
118 _drags.push_back (d);
122 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
124 d->set_manager (this);
125 _drags.push_back (d);
130 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
132 /* Prevent follow playhead during the drag to be nice to the user */
133 _old_follow_playhead = _editor->follow_playhead ();
134 _editor->set_follow_playhead (false);
136 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
138 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
139 (*i)->start_grab (e, c);
143 /** Call end_grab for each active drag.
144 * @return true if any drag reported movement having occurred.
147 DragManager::end_grab (GdkEvent* e)
152 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
153 bool const t = (*i)->end_grab (e);
164 _editor->set_follow_playhead (_old_follow_playhead, false);
170 DragManager::mark_double_click ()
172 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
173 (*i)->set_double_click (true);
178 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
182 /* calling this implies that we expect the event to have canvas
185 * Can we guarantee that this is true?
188 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
190 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
191 bool const t = (*i)->motion_handler (e, from_autoscroll);
192 /* run all handlers; return true if at least one of them
193 returns true (indicating that the event has been handled).
205 DragManager::have_item (ArdourCanvas::Item* i) const
207 list<Drag*>::const_iterator j = _drags.begin ();
208 while (j != _drags.end() && (*j)->item () != i) {
212 return j != _drags.end ();
215 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
218 , _pointer_frame_offset (0)
219 , _x_constrained (false)
220 , _y_constrained (false)
221 , _trackview_only (trackview_only)
222 , _move_threshold_passed (false)
223 , _starting_point_passed (false)
224 , _initially_vertical (false)
225 , _was_double_click (false)
226 , _raw_grab_frame (0)
228 , _last_pointer_frame (0)
235 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
241 _cursor_ctx = CursorContext::create (*_editor, cursor);
243 _cursor_ctx->change (cursor);
250 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
253 /* we set up x/y dragging constraints on first move */
255 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
257 setup_pointer_frame_offset ();
258 _grab_frame = adjusted_frame (_raw_grab_frame, event);
259 _last_pointer_frame = _grab_frame;
260 _last_pointer_x = _grab_x;
262 if (_trackview_only) {
263 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
266 _last_pointer_y = _grab_y;
270 if (!_editor->cursors()->is_invalid (cursor)) {
271 /* CAIROCANVAS need a variant here that passes *cursor */
272 _cursor_ctx = CursorContext::create (*_editor, cursor);
275 if (_editor->session() && _editor->session()->transport_rolling()) {
278 _was_rolling = false;
281 switch (_editor->snap_type()) {
282 case SnapToRegionStart:
283 case SnapToRegionEnd:
284 case SnapToRegionSync:
285 case SnapToRegionBoundary:
286 _editor->build_region_boundary_cache ();
293 /** Call to end a drag `successfully'. Ungrabs item and calls
294 * subclass' finished() method.
296 * @param event GDK event, or 0.
297 * @return true if some movement occurred, otherwise false.
300 Drag::end_grab (GdkEvent* event)
302 _editor->stop_canvas_autoscroll ();
306 finished (event, _move_threshold_passed);
308 _editor->verbose_cursor()->hide ();
311 return _move_threshold_passed;
315 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
319 if (f > _pointer_frame_offset) {
320 pos = f - _pointer_frame_offset;
324 _editor->snap_to_with_modifier (pos, event);
331 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
333 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
337 Drag::snap_delta (guint state) const
339 if (ArdourKeyboard::indicates_snap_delta (state)) {
347 Drag::current_pointer_x() const
349 return _drags->current_pointer_x ();
353 Drag::current_pointer_y () const
355 if (!_trackview_only) {
356 return _drags->current_pointer_y ();
359 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
363 Drag::setup_snap_delta (framepos_t pos)
365 framepos_t temp = pos;
366 _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
367 _snap_delta = temp - pos;
371 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
373 /* check to see if we have moved in any way that matters since the last motion event */
374 if (_move_threshold_passed &&
375 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
376 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
380 pair<framecnt_t, int> const threshold = move_threshold ();
382 bool const old_move_threshold_passed = _move_threshold_passed;
384 if (!_move_threshold_passed) {
386 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
387 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
389 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
392 if (active (_editor->mouse_mode) && _move_threshold_passed) {
394 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
396 if (old_move_threshold_passed != _move_threshold_passed) {
400 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
401 _initially_vertical = true;
403 _initially_vertical = false;
405 /** check constraints for this drag.
406 * Note that the current convention is to use "contains" for
407 * key modifiers during motion and "equals" when initiating a drag.
408 * In this case we haven't moved yet, so "equals" applies here.
410 if (Config->get_edit_mode() != Lock) {
411 if (event->motion.state & Gdk::BUTTON2_MASK) {
412 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
413 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
414 _x_constrained = false;
415 _y_constrained = true;
417 _x_constrained = true;
418 _y_constrained = false;
420 } else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
421 // if dragging normally, the motion is constrained to the first direction of movement.
422 if (_initially_vertical) {
423 _x_constrained = true;
424 _y_constrained = false;
426 _x_constrained = false;
427 _y_constrained = true;
431 if (event->button.state & Gdk::BUTTON2_MASK) {
432 _x_constrained = false;
434 _x_constrained = true;
436 _y_constrained = false;
440 if (!from_autoscroll) {
441 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
444 if (!_editor->autoscroll_active() || from_autoscroll) {
447 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
449 motion (event, first_move && !_starting_point_passed);
451 if (first_move && !_starting_point_passed) {
452 _starting_point_passed = true;
455 _last_pointer_x = _drags->current_pointer_x ();
456 _last_pointer_y = current_pointer_y ();
457 _last_pointer_frame = adjusted_current_frame (event);
467 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
475 aborted (_move_threshold_passed);
477 _editor->stop_canvas_autoscroll ();
478 _editor->verbose_cursor()->hide ();
482 Drag::show_verbose_cursor_time (framepos_t frame)
484 _editor->verbose_cursor()->set_time (frame);
485 _editor->verbose_cursor()->show ();
489 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
491 _editor->verbose_cursor()->set_duration (start, end);
492 _editor->verbose_cursor()->show ();
496 Drag::show_verbose_cursor_text (string const & text)
498 _editor->verbose_cursor()->set (text);
499 _editor->verbose_cursor()->show ();
502 boost::shared_ptr<Region>
503 Drag::add_midi_region (MidiTimeAxisView* view)
505 if (_editor->session()) {
506 const TempoMap& map (_editor->session()->tempo_map());
507 framecnt_t pos = grab_frame();
508 const Meter& m = map.meter_at (pos);
509 /* not that the frame rate used here can be affected by pull up/down which
512 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
513 return view->add_region (grab_frame(), len, true);
516 return boost::shared_ptr<Region>();
519 struct EditorOrderTimeAxisViewSorter {
520 bool operator() (TimeAxisView* a, TimeAxisView* b) {
521 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
522 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
524 return ra->route()->order_key () < rb->route()->order_key ();
528 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
533 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
535 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
536 as some of the regions we are dragging may be on such tracks.
539 TrackViewList track_views = _editor->track_views;
540 track_views.sort (EditorOrderTimeAxisViewSorter ());
542 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
543 _time_axis_views.push_back (*i);
545 TimeAxisView::Children children_list = (*i)->get_child_list ();
546 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
547 _time_axis_views.push_back (j->get());
551 /* the list of views can be empty at this point if this is a region list-insert drag
554 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
555 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
558 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
562 RegionDrag::region_going_away (RegionView* v)
564 list<DraggingView>::iterator i = _views.begin ();
565 while (i != _views.end() && i->view != v) {
569 if (i != _views.end()) {
574 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
575 * or -1 if it is not found.
578 RegionDrag::find_time_axis_view (TimeAxisView* t) const
581 int const N = _time_axis_views.size ();
582 while (i < N && _time_axis_views[i] != t) {
586 if (_time_axis_views[i] != t) {
593 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
594 : RegionDrag (e, i, p, v)
597 , _last_pointer_time_axis_view (0)
598 , _last_pointer_layer (0)
603 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
607 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
609 Drag::start_grab (event, cursor);
610 setup_snap_delta (_last_frame_position);
612 show_verbose_cursor_time (_last_frame_position);
614 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
616 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
617 assert(_last_pointer_time_axis_view >= 0);
618 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
622 /* cross track dragging seems broken here. disabled for now. */
623 _y_constrained = true;
628 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
630 /* compute the amount of pointer motion in frames, and where
631 the region would be if we moved it by that much.
633 *pending_region_position = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
635 framepos_t sync_frame;
636 framecnt_t sync_offset;
639 sync_offset = _primary->region()->sync_offset (sync_dir);
641 /* we don't handle a sync point that lies before zero.
643 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
645 sync_frame = *pending_region_position + (sync_dir * sync_offset);
647 _editor->snap_to_with_modifier (sync_frame, event);
649 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - snap_delta (event->button.state);
652 *pending_region_position = _last_frame_position;
655 if (*pending_region_position > max_framepos - _primary->region()->length()) {
656 *pending_region_position = _last_frame_position;
661 bool const x_move_allowed = !_x_constrained;
663 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
665 /* x movement since last time (in pixels) */
666 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
668 /* total x movement */
669 framecnt_t total_dx = *pending_region_position;
670 if (regions_came_from_canvas()) {
671 total_dx = total_dx - grab_frame ();
674 /* check that no regions have gone off the start of the session */
675 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
676 if ((i->view->region()->position() + total_dx) < 0) {
678 *pending_region_position = _last_frame_position;
689 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
695 const int tavsize = _time_axis_views.size();
696 const int dt = delta > 0 ? +1 : -1;
698 int target = start + delta - skip;
700 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
701 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
703 while (current >= 0 && current != target) {
705 if (current < 0 && dt < 0) {
708 if (current >= tavsize && dt > 0) {
711 if (current < 0 || current >= tavsize) {
715 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
716 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
720 if (distance_only && current == start + delta) {
728 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
730 if (_y_constrained) {
734 const int tavsize = _time_axis_views.size();
735 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
736 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
737 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
739 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
740 /* already in the drop zone */
741 if (delta_track >= 0) {
742 /* downward motion - OK if others are still not in the dropzone */
751 } else if (n >= tavsize) {
752 /* downward motion into drop zone. That's fine. */
756 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
757 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
758 /* not a track, or the wrong type */
762 double const l = i->layer + delta_layer;
764 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
765 mode to allow the user to place a region below another on layer 0.
767 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
768 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
769 If it has, the layers will be munged later anyway, so it's ok.
775 /* all regions being dragged are ok with this change */
779 struct DraggingViewSorter {
780 bool operator() (const DraggingView& a, const DraggingView& b) {
781 return a.time_axis_view < b.time_axis_view;
786 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
788 double delta_layer = 0;
789 int delta_time_axis_view = 0;
790 int current_pointer_time_axis_view = -1;
792 assert (!_views.empty ());
794 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
796 /* Find the TimeAxisView that the pointer is now over */
797 const double cur_y = current_pointer_y ();
798 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
799 TimeAxisView* tv = r.first;
801 if (!tv && cur_y < 0) {
802 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
806 /* find drop-zone y-position */
807 Coord last_track_bottom_edge;
808 last_track_bottom_edge = 0;
809 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
810 if (!(*t)->hidden()) {
811 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
816 if (tv && tv->view()) {
817 /* the mouse is over a track */
818 double layer = r.second;
820 if (first_move && tv->view()->layer_display() == Stacked) {
821 tv->view()->set_layer_display (Expanded);
824 /* Here's the current pointer position in terms of time axis view and layer */
825 current_pointer_time_axis_view = find_time_axis_view (tv);
826 assert(current_pointer_time_axis_view >= 0);
828 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
830 /* Work out the change in y */
832 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
833 if (!rtv || !rtv->is_track()) {
834 /* ignore busses early on. we can't move any regions on them */
835 } else if (_last_pointer_time_axis_view < 0) {
836 /* Was in the drop-zone, now over a track.
837 * Hence it must be an upward move (from the bottom)
839 * track_index is still -1, so delta must be set to
840 * move up the correct number of tracks from the bottom.
842 * This is necessary because steps may be skipped if
843 * the bottom-most track is not a valid target and/or
844 * if there are hidden tracks at the bottom.
845 * Hence the initial offset (_ddropzone) as well as the
846 * last valid pointer position (_pdropzone) need to be
847 * taken into account.
849 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
851 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
854 /* TODO needs adjustment per DraggingView,
856 * e.g. select one region on the top-layer of a track
857 * and one region which is at the bottom-layer of another track
860 * Indicated drop-zones and layering is wrong.
861 * and may infer additional layers on the target-track
862 * (depending how many layers the original track had).
864 * Or select two regions (different layers) on a same track,
865 * move across a non-layer track.. -> layering info is lost.
866 * on drop either of the regions may be on top.
868 * Proposed solution: screw it :) well,
869 * don't use delta_layer, use an absolute value
870 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
871 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
872 * 3) iterate over all DraggingView, find the one that is over the track with most layers
873 * 4) proportionally scale layer to layers available on target
875 delta_layer = current_pointer_layer - _last_pointer_layer;
878 /* for automation lanes, there is a TimeAxisView but no ->view()
879 * if (!tv) -> dropzone
881 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
882 /* Moving into the drop-zone.. */
883 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
884 /* delta_time_axis_view may not be sufficient to move into the DZ
885 * the mouse may enter it, but it may not be a valid move due to
888 * -> remember the delta needed to move into the dropzone
890 _ddropzone = delta_time_axis_view;
891 /* ..but subtract hidden tracks (or routes) at the bottom.
892 * we silently move mover them
894 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
895 - _time_axis_views.size();
897 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
898 /* move around inside the zone.
899 * This allows to move further down until all regions are in the zone.
901 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
902 assert(ptr_y >= last_track_bottom_edge);
903 assert(_ddropzone > 0);
905 /* calculate mouse position in 'tracks' below last track. */
906 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
907 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
909 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
911 delta_time_axis_view = dzpos - _pdropzone;
912 } else if (dzpos < _pdropzone && _ndropzone > 0) {
913 // move up inside the DZ
914 delta_time_axis_view = dzpos - _pdropzone;
918 /* Work out the change in x */
919 framepos_t pending_region_position;
920 double const x_delta = compute_x_delta (event, &pending_region_position);
921 _last_frame_position = pending_region_position;
923 /* calculate hidden tracks in current y-axis delta */
925 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
926 /* The mouse is more than one track below the dropzone.
927 * distance calculation is not needed (and would not work, either
928 * because the dropzone is "packed").
930 * Except when [partially] moving regions out of dropzone in a large step.
931 * (the mouse may or may not remain in the DZ)
932 * Hidden tracks at the bottom of the TAV need to be skipped.
934 * This also handles the case if the mouse entered the DZ
935 * in a large step (exessive delta), either due to fast-movement,
936 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
938 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
939 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
941 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
942 -_time_axis_views.size() - dt;
945 else if (_last_pointer_time_axis_view < 0) {
946 /* Moving out of the zone. Check for hidden tracks at the bottom. */
947 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
948 -_time_axis_views.size() - delta_time_axis_view;
950 /* calculate hidden tracks that are skipped by the pointer movement */
951 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
952 - _last_pointer_time_axis_view
953 - delta_time_axis_view;
956 /* Verify change in y */
957 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
958 /* this y movement is not allowed, so do no y movement this time */
959 delta_time_axis_view = 0;
964 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
965 /* haven't reached next snap point, and we're not switching
966 trackviews nor layers. nothing to do.
971 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
972 PlaylistDropzoneMap playlist_dropzone_map;
973 _ndropzone = 0; // number of elements currently in the dropzone
976 /* sort views by time_axis.
977 * This retains track order in the dropzone, regardless
978 * of actual selection order
980 _views.sort (DraggingViewSorter());
982 /* count number of distinct tracks of all regions
983 * being dragged, used for dropzone.
986 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
987 if (i->time_axis_view != prev_track) {
988 prev_track = i->time_axis_view;
994 _views.back().time_axis_view -
995 _views.front().time_axis_view;
997 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
998 - _views.back().time_axis_view;
1000 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1004 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1006 RegionView* rv = i->view;
1011 if (rv->region()->locked() || rv->region()->video_locked()) {
1018 /* reparent the regionview into a group above all
1022 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1023 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1024 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1025 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1026 /* move the item so that it continues to appear at the
1027 same location now that its parent has changed.
1029 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1032 /* If we have moved tracks, we'll fudge the layer delta so that the
1033 region gets moved back onto layer 0 on its new track; this avoids
1034 confusion when dragging regions from non-zero layers onto different
1037 double this_delta_layer = delta_layer;
1038 if (delta_time_axis_view != 0) {
1039 this_delta_layer = - i->layer;
1042 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1044 int track_index = i->time_axis_view + this_delta_time_axis_view;
1045 assert(track_index >= 0);
1047 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1048 /* Track is in the Dropzone */
1050 i->time_axis_view = track_index;
1051 assert(i->time_axis_view >= (int) _time_axis_views.size());
1054 double yposition = 0;
1055 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1056 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1059 /* store index of each new playlist as a negative count, starting at -1 */
1061 if (pdz == playlist_dropzone_map.end()) {
1062 /* compute where this new track (which doesn't exist yet) will live
1065 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1067 /* How high is this region view ? */
1069 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1070 ArdourCanvas::Rect bbox;
1073 bbox = obbox.get ();
1076 last_track_bottom_edge += bbox.height();
1078 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1081 yposition = pdz->second;
1084 /* values are zero or negative, hence the use of min() */
1085 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1090 /* The TimeAxisView that this region is now over */
1091 TimeAxisView* current_tv = _time_axis_views[track_index];
1093 /* Ensure it is moved from stacked -> expanded if appropriate */
1094 if (current_tv->view()->layer_display() == Stacked) {
1095 current_tv->view()->set_layer_display (Expanded);
1098 /* We're only allowed to go -ve in layer on Expanded views */
1099 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1100 this_delta_layer = - i->layer;
1104 rv->set_height (current_tv->view()->child_height ());
1106 /* Update show/hidden status as the region view may have come from a hidden track,
1107 or have moved to one.
1109 if (current_tv->hidden ()) {
1110 rv->get_canvas_group()->hide ();
1112 rv->get_canvas_group()->show ();
1115 /* Update the DraggingView */
1116 i->time_axis_view = track_index;
1117 i->layer += this_delta_layer;
1120 _editor->mouse_brush_insert_region (rv, pending_region_position);
1124 /* Get the y coordinate of the top of the track that this region is now over */
1125 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1127 /* And adjust for the layer that it should be on */
1128 StreamView* cv = current_tv->view ();
1129 switch (cv->layer_display ()) {
1133 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1136 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1140 /* need to get the parent of the regionview
1141 * canvas group and get its position in
1142 * equivalent coordinate space as the trackview
1143 * we are now dragging over.
1146 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1151 /* Now move the region view */
1152 rv->move (x_delta, y_delta);
1154 } /* foreach region */
1156 _total_x_delta += x_delta;
1158 if (x_delta != 0 && !_brushing) {
1159 show_verbose_cursor_time (_last_frame_position);
1162 /* keep track of pointer movement */
1164 /* the pointer is currently over a time axis view */
1166 if (_last_pointer_time_axis_view < 0) {
1167 /* last motion event was not over a time axis view
1168 * or last y-movement out of the dropzone was not valid
1171 if (delta_time_axis_view < 0) {
1172 /* in the drop zone, moving up */
1174 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1175 * We do not use negative _last_pointer_time_axis_view because
1176 * the dropzone is "packed" (the actual track offset is ignored)
1178 * As opposed to the actual number
1179 * of elements in the dropzone (_ndropzone)
1180 * _pdropzone is not constrained. This is necessary
1181 * to allow moving multiple regions with y-distance
1184 * There can be 0 elements in the dropzone,
1185 * even though the drag-pointer is inside the DZ.
1188 * [ Audio-track, Midi-track, Audio-track, DZ ]
1189 * move regions from both audio tracks at the same time into the
1190 * DZ by grabbing the region in the bottom track.
1192 assert(current_pointer_time_axis_view >= 0);
1193 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1197 /* only move out of the zone if the movement is OK */
1198 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1199 assert(delta_time_axis_view < 0);
1200 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1201 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1202 * the current position can be calculated as follows:
1204 // a well placed oofus attack can still throw this off.
1205 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1206 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1209 /* last motion event was also over a time axis view */
1210 _last_pointer_time_axis_view += delta_time_axis_view;
1211 assert(_last_pointer_time_axis_view >= 0);
1216 /* the pointer is not over a time axis view */
1217 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1218 _pdropzone += delta_time_axis_view - delta_skip;
1219 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1222 _last_pointer_layer += delta_layer;
1226 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1228 if (_copy && first_move) {
1229 if (_x_constrained && !_brushing) {
1230 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1231 } else if (!_brushing) {
1232 _editor->begin_reversible_command (Operations::region_copy);
1233 } else if (_brushing) {
1234 _editor->begin_reversible_command (Operations::drag_region_brush);
1236 /* duplicate the regionview(s) and region(s) */
1238 list<DraggingView> new_regionviews;
1240 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1242 RegionView* rv = i->view;
1243 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1244 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1246 const boost::shared_ptr<const Region> original = rv->region();
1247 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1248 region_copy->set_position (original->position());
1249 /* need to set this so that the drop zone code can work. This doesn't
1250 actually put the region into the playlist, but just sets a weak pointer
1253 region_copy->set_playlist (original->playlist());
1257 boost::shared_ptr<AudioRegion> audioregion_copy
1258 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1260 nrv = new AudioRegionView (*arv, audioregion_copy);
1262 boost::shared_ptr<MidiRegion> midiregion_copy
1263 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1264 nrv = new MidiRegionView (*mrv, midiregion_copy);
1269 nrv->get_canvas_group()->show ();
1270 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1272 /* swap _primary to the copy */
1274 if (rv == _primary) {
1278 /* ..and deselect the one we copied */
1280 rv->set_selected (false);
1283 if (!new_regionviews.empty()) {
1285 /* reflect the fact that we are dragging the copies */
1287 _views = new_regionviews;
1289 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1292 } else if (!_copy && first_move) {
1293 if (_x_constrained && !_brushing) {
1294 _editor->begin_reversible_command (_("fixed time region drag"));
1295 } else if (!_brushing) {
1296 _editor->begin_reversible_command (Operations::region_drag);
1297 } else if (_brushing) {
1298 _editor->begin_reversible_command (Operations::drag_region_brush);
1301 RegionMotionDrag::motion (event, first_move);
1305 RegionMotionDrag::finished (GdkEvent *, bool)
1307 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1308 if (!(*i)->view()) {
1312 if ((*i)->view()->layer_display() == Expanded) {
1313 (*i)->view()->set_layer_display (Stacked);
1319 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1321 RegionMotionDrag::finished (ev, movement_occurred);
1323 if (!movement_occurred) {
1327 if (was_double_click() && !_views.empty()) {
1328 DraggingView dv = _views.front();
1329 dv.view->show_region_editor ();
1336 assert (!_views.empty ());
1338 /* We might have hidden region views so that they weren't visible during the drag
1339 (when they have been reparented). Now everything can be shown again, as region
1340 views are back in their track parent groups.
1342 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1343 i->view->get_canvas_group()->show ();
1346 bool const changed_position = (_last_frame_position != _primary->region()->position());
1347 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1348 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1368 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1372 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1374 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1379 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1380 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1381 uint32_t output_chan = region->n_channels();
1382 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1383 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1385 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name());
1386 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1388 rtav->set_height (original->current_height());
1392 ChanCount one_midi_port (DataType::MIDI, 1);
1393 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1394 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1395 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1397 rtav->set_height (original->current_height());
1402 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1408 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1410 RegionSelection new_views;
1411 PlaylistSet modified_playlists;
1412 RouteTimeAxisView* new_time_axis_view = 0;
1415 /* all changes were made during motion event handlers */
1417 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1421 _editor->commit_reversible_command ();
1425 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1426 PlaylistMapping playlist_mapping;
1428 /* insert the regions into their new playlists */
1429 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1431 RouteTimeAxisView* dest_rtv = 0;
1433 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1439 if (changed_position && !_x_constrained) {
1440 where = i->view->region()->position() - drag_delta;
1442 where = i->view->region()->position();
1445 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1446 /* dragged to drop zone */
1448 PlaylistMapping::iterator pm;
1450 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1451 /* first region from this original playlist: create a new track */
1452 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1453 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1454 dest_rtv = new_time_axis_view;
1456 /* we already created a new track for regions from this playlist, use it */
1457 dest_rtv = pm->second;
1460 /* destination time axis view is the one we dragged to */
1461 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1464 if (dest_rtv != 0) {
1465 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1466 if (new_view != 0) {
1467 new_views.push_back (new_view);
1471 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1472 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1475 list<DraggingView>::const_iterator next = i;
1481 /* If we've created new regions either by copying or moving
1482 to a new track, we want to replace the old selection with the new ones
1485 if (new_views.size() > 0) {
1486 _editor->selection->set (new_views);
1489 /* write commands for the accumulated diffs for all our modified playlists */
1490 add_stateful_diff_commands_for_playlists (modified_playlists);
1492 _editor->commit_reversible_command ();
1496 RegionMoveDrag::finished_no_copy (
1497 bool const changed_position,
1498 bool const changed_tracks,
1499 framecnt_t const drag_delta
1502 RegionSelection new_views;
1503 PlaylistSet modified_playlists;
1504 PlaylistSet frozen_playlists;
1505 set<RouteTimeAxisView*> views_to_update;
1506 RouteTimeAxisView* new_time_axis_view = 0;
1508 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1509 PlaylistMapping playlist_mapping;
1511 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1513 RegionView* rv = i->view;
1514 RouteTimeAxisView* dest_rtv = 0;
1516 if (rv->region()->locked() || rv->region()->video_locked()) {
1521 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1522 /* dragged to drop zone */
1524 PlaylistMapping::iterator pm;
1526 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1527 /* first region from this original playlist: create a new track */
1528 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1529 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1530 dest_rtv = new_time_axis_view;
1532 /* we already created a new track for regions from this playlist, use it */
1533 dest_rtv = pm->second;
1537 /* destination time axis view is the one we dragged to */
1538 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1543 double const dest_layer = i->layer;
1545 views_to_update.insert (dest_rtv);
1549 if (changed_position && !_x_constrained) {
1550 where = rv->region()->position() - drag_delta;
1552 where = rv->region()->position();
1555 if (changed_tracks) {
1557 /* insert into new playlist */
1559 RegionView* new_view = insert_region_into_playlist (
1560 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1563 if (new_view == 0) {
1568 new_views.push_back (new_view);
1570 /* remove from old playlist */
1572 /* the region that used to be in the old playlist is not
1573 moved to the new one - we use a copy of it. as a result,
1574 any existing editor for the region should no longer be
1577 rv->hide_region_editor();
1580 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1584 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1586 /* this movement may result in a crossfade being modified, or a layering change,
1587 so we need to get undo data from the playlist as well as the region.
1590 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1592 playlist->clear_changes ();
1595 rv->region()->clear_changes ();
1598 motion on the same track. plonk the previously reparented region
1599 back to its original canvas group (its streamview).
1600 No need to do anything for copies as they are fake regions which will be deleted.
1603 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1604 rv->get_canvas_group()->set_y_position (i->initial_y);
1607 /* just change the model */
1608 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1609 playlist->set_layer (rv->region(), dest_layer);
1612 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1614 r = frozen_playlists.insert (playlist);
1617 playlist->freeze ();
1620 rv->region()->set_position (where);
1621 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1624 if (changed_tracks) {
1626 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1627 was selected in all of them, then removing it from a playlist will have removed all
1628 trace of it from _views (i.e. there were N regions selected, we removed 1,
1629 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1630 corresponding regionview, and _views is now empty).
1632 This could have invalidated any and all iterators into _views.
1634 The heuristic we use here is: if the region selection is empty, break out of the loop
1635 here. if the region selection is not empty, then restart the loop because we know that
1636 we must have removed at least the region(view) we've just been working on as well as any
1637 that we processed on previous iterations.
1639 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1640 we can just iterate.
1644 if (_views.empty()) {
1655 /* If we've created new regions either by copying or moving
1656 to a new track, we want to replace the old selection with the new ones
1659 if (new_views.size() > 0) {
1660 _editor->selection->set (new_views);
1663 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1667 /* write commands for the accumulated diffs for all our modified playlists */
1668 add_stateful_diff_commands_for_playlists (modified_playlists);
1669 /* applies to _brushing */
1670 _editor->commit_reversible_command ();
1672 /* We have futzed with the layering of canvas items on our streamviews.
1673 If any region changed layer, this will have resulted in the stream
1674 views being asked to set up their region views, and all will be well.
1675 If not, we might now have badly-ordered region views. Ask the StreamViews
1676 involved to sort themselves out, just in case.
1679 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1680 (*i)->view()->playlist_layered ((*i)->track ());
1684 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1685 * @param region Region to remove.
1686 * @param playlist playlist To remove from.
1687 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1688 * that clear_changes () is only called once per playlist.
1691 RegionMoveDrag::remove_region_from_playlist (
1692 boost::shared_ptr<Region> region,
1693 boost::shared_ptr<Playlist> playlist,
1694 PlaylistSet& modified_playlists
1697 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1700 playlist->clear_changes ();
1703 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1707 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1708 * clearing the playlist's diff history first if necessary.
1709 * @param region Region to insert.
1710 * @param dest_rtv Destination RouteTimeAxisView.
1711 * @param dest_layer Destination layer.
1712 * @param where Destination position.
1713 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1714 * that clear_changes () is only called once per playlist.
1715 * @return New RegionView, or 0 if no insert was performed.
1718 RegionMoveDrag::insert_region_into_playlist (
1719 boost::shared_ptr<Region> region,
1720 RouteTimeAxisView* dest_rtv,
1723 PlaylistSet& modified_playlists
1726 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1727 if (!dest_playlist) {
1731 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1732 _new_region_view = 0;
1733 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1735 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1736 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1738 dest_playlist->clear_changes ();
1741 dest_playlist->add_region (region, where);
1743 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1744 dest_playlist->set_layer (region, dest_layer);
1749 assert (_new_region_view);
1751 return _new_region_view;
1755 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1757 _new_region_view = rv;
1761 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1763 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1764 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1766 _editor->session()->add_command (c);
1775 RegionMoveDrag::aborted (bool movement_occurred)
1779 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1780 list<DraggingView>::const_iterator next = i;
1789 RegionMotionDrag::aborted (movement_occurred);
1794 RegionMotionDrag::aborted (bool)
1796 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1798 StreamView* sview = (*i)->view();
1801 if (sview->layer_display() == Expanded) {
1802 sview->set_layer_display (Stacked);
1807 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1808 RegionView* rv = i->view;
1809 TimeAxisView* tv = &(rv->get_time_axis_view ());
1810 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1812 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1813 rv->get_canvas_group()->set_y_position (0);
1815 rv->move (-_total_x_delta, 0);
1816 rv->set_height (rtv->view()->child_height ());
1820 /** @param b true to brush, otherwise false.
1821 * @param c true to make copies of the regions being moved, otherwise false.
1823 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1824 : RegionMotionDrag (e, i, p, v, b)
1827 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1830 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1831 if (rtv && rtv->is_track()) {
1832 speed = rtv->track()->speed ();
1835 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1839 RegionMoveDrag::setup_pointer_frame_offset ()
1841 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1844 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1845 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1847 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1849 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1850 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1852 _primary = v->view()->create_region_view (r, false, false);
1854 _primary->get_canvas_group()->show ();
1855 _primary->set_position (pos, 0);
1856 _views.push_back (DraggingView (_primary, this, v));
1858 _last_frame_position = pos;
1860 _item = _primary->get_canvas_group ();
1864 RegionInsertDrag::finished (GdkEvent *, bool)
1866 int pos = _views.front().time_axis_view;
1867 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1869 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1871 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1872 _primary->get_canvas_group()->set_y_position (0);
1874 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1876 _editor->begin_reversible_command (Operations::insert_region);
1877 playlist->clear_changes ();
1878 playlist->add_region (_primary->region (), _last_frame_position);
1880 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1881 if (Config->get_edit_mode() == Ripple) {
1882 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1885 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1886 _editor->commit_reversible_command ();
1894 RegionInsertDrag::aborted (bool)
1901 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1902 : RegionMoveDrag (e, i, p, v, false, false)
1904 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1907 struct RegionSelectionByPosition {
1908 bool operator() (RegionView*a, RegionView* b) {
1909 return a->region()->position () < b->region()->position();
1914 RegionSpliceDrag::motion (GdkEvent* event, bool)
1916 /* Which trackview is this ? */
1918 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1919 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1921 /* The region motion is only processed if the pointer is over
1925 if (!tv || !tv->is_track()) {
1926 /* To make sure we hide the verbose canvas cursor when the mouse is
1927 not held over an audio track.
1929 _editor->verbose_cursor()->hide ();
1932 _editor->verbose_cursor()->show ();
1937 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1943 RegionSelection copy;
1944 _editor->selection->regions.by_position(copy);
1946 framepos_t const pf = adjusted_current_frame (event);
1948 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1950 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1956 boost::shared_ptr<Playlist> playlist;
1958 if ((playlist = atv->playlist()) == 0) {
1962 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1967 if (pf < (*i)->region()->last_frame() + 1) {
1971 if (pf > (*i)->region()->first_frame()) {
1977 playlist->shuffle ((*i)->region(), dir);
1982 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1984 RegionMoveDrag::finished (event, movement_occurred);
1988 RegionSpliceDrag::aborted (bool)
1998 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2001 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2003 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2004 RegionSelection to_ripple;
2005 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2006 if ((*i)->position() >= where) {
2007 to_ripple.push_back (rtv->view()->find_view(*i));
2011 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2012 if (!exclude.contains (*i)) {
2013 // the selection has already been added to _views
2015 if (drag_in_progress) {
2016 // do the same things that RegionMotionDrag::motion does when
2017 // first_move is true, for the region views that we're adding
2018 // to _views this time
2021 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2022 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2023 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2024 rvg->reparent (_editor->_drag_motion_group);
2026 // we only need to move in the y direction
2027 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2032 _views.push_back (DraggingView (*i, this, tav));
2038 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2041 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2042 // we added all the regions after the selection
2044 std::list<DraggingView>::iterator to_erase = i++;
2045 if (!_editor->selection->regions.contains (to_erase->view)) {
2046 // restore the non-selected regions to their original playlist & positions,
2047 // and then ripple them back by the length of the regions that were dragged away
2048 // do the same things as RegionMotionDrag::aborted
2050 RegionView *rv = to_erase->view;
2051 TimeAxisView* tv = &(rv->get_time_axis_view ());
2052 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2055 // plonk them back onto their own track
2056 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2057 rv->get_canvas_group()->set_y_position (0);
2061 // move the underlying region to match the view
2062 rv->region()->set_position (rv->region()->position() + amount);
2064 // restore the view to match the underlying region's original position
2065 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2068 rv->set_height (rtv->view()->child_height ());
2069 _views.erase (to_erase);
2075 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2077 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2079 return allow_moves_across_tracks;
2087 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2088 : RegionMoveDrag (e, i, p, v, false, false)
2090 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2091 // compute length of selection
2092 RegionSelection selected_regions = _editor->selection->regions;
2093 selection_length = selected_regions.end_frame() - selected_regions.start();
2095 // we'll only allow dragging to another track in ripple mode if all the regions
2096 // being dragged start off on the same track
2097 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2100 exclude = new RegionList;
2101 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2102 exclude->push_back((*i)->region());
2105 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2106 RegionSelection copy;
2107 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2109 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2110 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2112 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2113 // find ripple start point on each applicable playlist
2114 RegionView *first_selected_on_this_track = NULL;
2115 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2116 if ((*i)->region()->playlist() == (*pi)) {
2117 // region is on this playlist - it's the first, because they're sorted
2118 first_selected_on_this_track = *i;
2122 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2123 add_all_after_to_views (
2124 &first_selected_on_this_track->get_time_axis_view(),
2125 first_selected_on_this_track->region()->position(),
2126 selected_regions, false);
2129 if (allow_moves_across_tracks) {
2130 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2138 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2140 /* Which trackview is this ? */
2142 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2143 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2145 /* The region motion is only processed if the pointer is over
2149 if (!tv || !tv->is_track()) {
2150 /* To make sure we hide the verbose canvas cursor when the mouse is
2151 not held over an audiotrack.
2153 _editor->verbose_cursor()->hide ();
2157 framepos_t where = adjusted_current_frame (event);
2158 assert (where >= 0);
2160 double delta = compute_x_delta (event, &after);
2162 framecnt_t amount = _editor->pixel_to_sample (delta);
2164 if (allow_moves_across_tracks) {
2165 // all the originally selected regions were on the same track
2167 framecnt_t adjust = 0;
2168 if (prev_tav && tv != prev_tav) {
2169 // dragged onto a different track
2170 // remove the unselected regions from _views, restore them to their original positions
2171 // and add the regions after the drop point on the new playlist to _views instead.
2172 // undo the effect of rippling the previous playlist, and include the effect of removing
2173 // the dragged region(s) from this track
2175 remove_unselected_from_views (prev_amount, false);
2176 // ripple previous playlist according to the regions that have been removed onto the new playlist
2177 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2180 // move just the selected regions
2181 RegionMoveDrag::motion(event, first_move);
2183 // ensure that the ripple operation on the new playlist inserts selection_length time
2184 adjust = selection_length;
2185 // ripple the new current playlist
2186 tv->playlist()->ripple (where, amount+adjust, exclude);
2188 // add regions after point where drag entered this track to subsequent ripples
2189 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2192 // motion on same track
2193 RegionMoveDrag::motion(event, first_move);
2197 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2198 prev_position = where;
2200 // selection encompasses multiple tracks - just drag
2201 // cross-track drags are forbidden
2202 RegionMoveDrag::motion(event, first_move);
2205 if (!_x_constrained) {
2206 prev_amount += amount;
2209 _last_frame_position = after;
2213 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2215 if (!movement_occurred) {
2219 if (was_double_click() && !_views.empty()) {
2220 DraggingView dv = _views.front();
2221 dv.view->show_region_editor ();
2228 _editor->begin_reversible_command(_("Ripple drag"));
2230 // remove the regions being rippled from the dragging view, updating them to
2231 // their new positions
2232 remove_unselected_from_views (prev_amount, true);
2234 if (allow_moves_across_tracks) {
2236 // if regions were dragged across tracks, we've rippled any later
2237 // regions on the track the regions were dragged off, so we need
2238 // to add the original track to the undo record
2239 orig_tav->playlist()->clear_changes();
2240 vector<Command*> cmds;
2241 orig_tav->playlist()->rdiff (cmds);
2242 _editor->session()->add_commands (cmds);
2244 if (prev_tav && prev_tav != orig_tav) {
2245 prev_tav->playlist()->clear_changes();
2246 vector<Command*> cmds;
2247 prev_tav->playlist()->rdiff (cmds);
2248 _editor->session()->add_commands (cmds);
2251 // selection spanned multiple tracks - all will need adding to undo record
2253 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2254 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2256 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2257 (*pi)->clear_changes();
2258 vector<Command*> cmds;
2259 (*pi)->rdiff (cmds);
2260 _editor->session()->add_commands (cmds);
2264 // other modified playlists are added to undo by RegionMoveDrag::finished()
2265 RegionMoveDrag::finished (event, movement_occurred);
2266 _editor->commit_reversible_command();
2270 RegionRippleDrag::aborted (bool movement_occurred)
2272 RegionMoveDrag::aborted (movement_occurred);
2277 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2279 _view (dynamic_cast<MidiTimeAxisView*> (v))
2281 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2287 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2290 _region = add_midi_region (_view);
2291 _view->playlist()->freeze ();
2294 framepos_t const f = adjusted_current_frame (event);
2295 if (f < grab_frame()) {
2296 _region->set_position (f);
2299 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2300 so that if this region is duplicated, its duplicate starts on
2301 a snap point rather than 1 frame after a snap point. Otherwise things get
2302 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2303 place snapped notes at the start of the region.
2306 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2307 _region->set_length (len < 1 ? 1 : len);
2313 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2315 if (!movement_occurred) {
2316 add_midi_region (_view);
2318 _view->playlist()->thaw ();
2323 RegionCreateDrag::aborted (bool)
2326 _view->playlist()->thaw ();
2332 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2339 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2343 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2345 Gdk::Cursor* cursor;
2346 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2348 float x_fraction = cnote->mouse_x_fraction ();
2350 if (x_fraction > 0.0 && x_fraction < 0.25) {
2351 cursor = _editor->cursors()->left_side_trim;
2354 cursor = _editor->cursors()->right_side_trim;
2358 Drag::start_grab (event, cursor);
2360 region = &cnote->region_view();
2363 temp = region->snap_to_pixel (cnote->x0 (), true);
2364 _snap_delta = temp - cnote->x0 ();
2368 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2373 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2374 if (ms.size() > 1) {
2375 /* has to be relative, may make no sense otherwise */
2378 /* select this note; if it is already selected, preserve the existing selection,
2379 otherwise make this note the only one selected.
2381 region->note_selected (cnote, cnote->selected ());
2385 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2387 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2389 _editor->begin_reversible_command (_("resize notes"));
2391 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2392 MidiRegionSelection::iterator next;
2395 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2397 mrv->begin_resizing (at_front);
2403 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2404 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2406 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2410 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2412 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2413 if (_editor->snap_mode () != SnapOff) {
2417 if (_editor->snap_mode () == SnapOff) {
2419 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2420 if (apply_snap_delta) {
2426 if (apply_snap_delta) {
2430 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2436 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2438 if (!movement_occurred) {
2442 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2443 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2444 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2446 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2449 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2451 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2452 if (_editor->snap_mode () != SnapOff) {
2456 if (_editor->snap_mode () == SnapOff) {
2458 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2459 if (apply_snap_delta) {
2465 if (apply_snap_delta) {
2469 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2473 _editor->commit_reversible_command ();
2477 NoteResizeDrag::aborted (bool)
2479 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2480 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2481 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2483 mrv->abort_resizing ();
2488 AVDraggingView::AVDraggingView (RegionView* v)
2491 initial_position = v->region()->position ();
2494 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2497 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2500 TrackViewList empty;
2502 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2503 std::list<RegionView*> views = rs.by_layer();
2505 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2506 RegionView* rv = (*i);
2507 if (!rv->region()->video_locked()) {
2510 _views.push_back (AVDraggingView (rv));
2515 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2517 Drag::start_grab (event);
2518 if (_editor->session() == 0) {
2522 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2523 _max_backwards_drag = (
2524 ARDOUR_UI::instance()->video_timeline->get_duration()
2525 + ARDOUR_UI::instance()->video_timeline->get_offset()
2526 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2529 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2530 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2531 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2534 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2537 Timecode::Time timecode;
2538 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2539 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);
2540 show_verbose_cursor_text (buf);
2544 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2546 if (_editor->session() == 0) {
2549 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2553 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2554 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2556 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2557 dt = - _max_backwards_drag;
2560 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2561 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2563 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2564 RegionView* rv = i->view;
2565 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2568 rv->region()->clear_changes ();
2569 rv->region()->suspend_property_changes();
2571 rv->region()->set_position(i->initial_position + dt);
2572 rv->region_changed(ARDOUR::Properties::position);
2575 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2576 Timecode::Time timecode;
2577 Timecode::Time timediff;
2579 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2580 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2581 snprintf (buf, sizeof (buf),
2582 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2583 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2584 , _("Video Start:"),
2585 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2587 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2589 show_verbose_cursor_text (buf);
2593 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2595 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2599 if (!movement_occurred || ! _editor->session()) {
2603 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2605 _editor->begin_reversible_command (_("Move Video"));
2607 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2608 ARDOUR_UI::instance()->video_timeline->save_undo();
2609 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2610 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2612 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2613 i->view->drag_end();
2614 i->view->region()->resume_property_changes ();
2616 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2619 _editor->session()->maybe_update_session_range(
2620 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2621 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2625 _editor->commit_reversible_command ();
2629 VideoTimeLineDrag::aborted (bool)
2631 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2634 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2635 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2637 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2638 i->view->region()->resume_property_changes ();
2639 i->view->region()->set_position(i->initial_position);
2643 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2644 : RegionDrag (e, i, p, v)
2645 , _preserve_fade_anchor (preserve_fade_anchor)
2646 , _jump_position_when_done (false)
2648 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2652 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2655 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2656 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2658 if (tv && tv->is_track()) {
2659 speed = tv->track()->speed();
2662 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2663 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2664 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2666 framepos_t const pf = adjusted_current_frame (event);
2667 setup_snap_delta (region_start);
2669 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2670 /* Move the contents of the region around without changing the region bounds */
2671 _operation = ContentsTrim;
2672 Drag::start_grab (event, _editor->cursors()->trimmer);
2674 /* These will get overridden for a point trim.*/
2675 if (pf < (region_start + region_length/2)) {
2676 /* closer to front */
2677 _operation = StartTrim;
2678 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2679 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2681 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2685 _operation = EndTrim;
2686 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2687 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2689 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2693 /* jump trim disabled for now
2694 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2695 _jump_position_when_done = true;
2699 switch (_operation) {
2701 show_verbose_cursor_time (region_start);
2702 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2703 i->view->trim_front_starting ();
2707 show_verbose_cursor_duration (region_start, region_end);
2710 show_verbose_cursor_time (pf);
2714 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2715 i->view->region()->suspend_property_changes ();
2720 TrimDrag::motion (GdkEvent* event, bool first_move)
2722 RegionView* rv = _primary;
2725 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2726 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2727 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2728 frameoffset_t frame_delta = 0;
2730 if (tv && tv->is_track()) {
2731 speed = tv->track()->speed();
2733 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2734 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2740 switch (_operation) {
2742 trim_type = "Region start trim";
2745 trim_type = "Region end trim";
2748 trim_type = "Region content trim";
2755 _editor->begin_reversible_command (trim_type);
2757 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2758 RegionView* rv = i->view;
2759 rv->enable_display (false);
2760 rv->region()->playlist()->clear_owned_changes ();
2762 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2765 arv->temporarily_hide_envelope ();
2769 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2770 insert_result = _editor->motion_frozen_playlists.insert (pl);
2772 if (insert_result.second) {
2778 bool non_overlap_trim = false;
2780 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2781 non_overlap_trim = true;
2784 /* contstrain trim to fade length */
2785 if (_preserve_fade_anchor) {
2786 switch (_operation) {
2788 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2789 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2791 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2792 if (ar->locked()) continue;
2793 framecnt_t len = ar->fade_in()->back()->when;
2794 if (len < dt) dt = min(dt, len);
2798 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2799 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2801 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2802 if (ar->locked()) continue;
2803 framecnt_t len = ar->fade_out()->back()->when;
2804 if (len < -dt) dt = max(dt, -len);
2813 switch (_operation) {
2815 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2816 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2817 if (changed && _preserve_fade_anchor) {
2818 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2820 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2821 framecnt_t len = ar->fade_in()->back()->when;
2822 framecnt_t diff = ar->first_frame() - i->initial_position;
2823 framepos_t new_length = len - diff;
2824 i->anchored_fade_length = min (ar->length(), new_length);
2825 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2826 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2833 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2834 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2835 if (changed && _preserve_fade_anchor) {
2836 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2838 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2839 framecnt_t len = ar->fade_out()->back()->when;
2840 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2841 framepos_t new_length = len + diff;
2842 i->anchored_fade_length = min (ar->length(), new_length);
2843 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2844 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2852 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2854 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2855 i->view->move_contents (frame_delta);
2861 switch (_operation) {
2863 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2866 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2869 // show_verbose_cursor_time (frame_delta);
2875 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2877 if (movement_occurred) {
2878 motion (event, false);
2880 if (_operation == StartTrim) {
2881 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2883 /* This must happen before the region's StatefulDiffCommand is created, as it may
2884 `correct' (ahem) the region's _start from being negative to being zero. It
2885 needs to be zero in the undo record.
2887 i->view->trim_front_ending ();
2889 if (_preserve_fade_anchor) {
2890 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2892 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2893 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2894 ar->set_fade_in_length(i->anchored_fade_length);
2895 ar->set_fade_in_active(true);
2898 if (_jump_position_when_done) {
2899 i->view->region()->set_position (i->initial_position);
2902 } else if (_operation == EndTrim) {
2903 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2904 if (_preserve_fade_anchor) {
2905 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2907 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2908 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2909 ar->set_fade_out_length(i->anchored_fade_length);
2910 ar->set_fade_out_active(true);
2913 if (_jump_position_when_done) {
2914 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2919 if (!_views.empty()) {
2920 if (_operation == StartTrim) {
2921 _editor->maybe_locate_with_edit_preroll(
2922 _views.begin()->view->region()->position());
2924 if (_operation == EndTrim) {
2925 _editor->maybe_locate_with_edit_preroll(
2926 _views.begin()->view->region()->position() +
2927 _views.begin()->view->region()->length());
2931 if (!_editor->selection->selected (_primary)) {
2932 _primary->thaw_after_trim ();
2935 set<boost::shared_ptr<Playlist> > diffed_playlists;
2937 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2938 i->view->thaw_after_trim ();
2939 i->view->enable_display (true);
2941 /* Trimming one region may affect others on the playlist, so we need
2942 to get undo Commands from the whole playlist rather than just the
2943 region. Use diffed_playlists to make sure we don't diff a given
2944 playlist more than once.
2946 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2947 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2948 vector<Command*> cmds;
2950 _editor->session()->add_commands (cmds);
2951 diffed_playlists.insert (p);
2956 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2960 _editor->motion_frozen_playlists.clear ();
2961 _editor->commit_reversible_command();
2964 /* no mouse movement */
2965 _editor->point_trim (event, adjusted_current_frame (event));
2968 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2969 if (_operation == StartTrim) {
2970 i->view->trim_front_ending ();
2973 i->view->region()->resume_property_changes ();
2978 TrimDrag::aborted (bool movement_occurred)
2980 /* Our motion method is changing model state, so use the Undo system
2981 to cancel. Perhaps not ideal, as this will leave an Undo point
2982 behind which may be slightly odd from the user's point of view.
2987 if (movement_occurred) {
2991 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2992 i->view->region()->resume_property_changes ();
2997 TrimDrag::setup_pointer_frame_offset ()
2999 list<DraggingView>::iterator i = _views.begin ();
3000 while (i != _views.end() && i->view != _primary) {
3004 if (i == _views.end()) {
3008 switch (_operation) {
3010 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3013 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3020 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3024 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3025 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3030 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3032 Drag::start_grab (event, cursor);
3033 show_verbose_cursor_time (adjusted_current_frame(event));
3037 MeterMarkerDrag::setup_pointer_frame_offset ()
3039 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3043 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3045 if (!_marker->meter().movable()) {
3051 // create a dummy marker for visual representation of moving the
3052 // section, because whether its a copy or not, we're going to
3053 // leave or lose the original marker (leave if its a copy; lose if its
3054 // not, because we'll remove it from the map).
3056 MeterSection section (_marker->meter());
3058 if (!section.movable()) {
3063 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3065 _marker = new MeterMarker (
3067 *_editor->meter_group,
3068 ARDOUR_UI::config()->color ("meter marker"),
3070 *new MeterSection (_marker->meter())
3073 /* use the new marker for the grab */
3074 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3077 TempoMap& map (_editor->session()->tempo_map());
3078 /* get current state */
3079 before_state = &map.get_state();
3080 /* remove the section while we drag it */
3081 map.remove_meter (section, true);
3085 framepos_t const pf = adjusted_current_frame (event);
3087 _marker->set_position (pf);
3088 show_verbose_cursor_time (pf);
3092 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3094 if (!movement_occurred) {
3095 if (was_double_click()) {
3096 _editor->edit_meter_marker (*_marker);
3101 if (!_marker->meter().movable()) {
3105 motion (event, false);
3107 Timecode::BBT_Time when;
3109 TempoMap& map (_editor->session()->tempo_map());
3110 map.bbt_time (last_pointer_frame(), when);
3112 if (_copy == true) {
3113 _editor->begin_reversible_command (_("copy meter mark"));
3114 XMLNode &before = map.get_state();
3115 map.add_meter (_marker->meter(), when);
3116 XMLNode &after = map.get_state();
3117 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3118 _editor->commit_reversible_command ();
3121 _editor->begin_reversible_command (_("move meter mark"));
3123 /* we removed it before, so add it back now */
3125 map.add_meter (_marker->meter(), when);
3126 XMLNode &after = map.get_state();
3127 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3128 _editor->commit_reversible_command ();
3131 // delete the dummy marker we used for visual representation while moving.
3132 // a new visual marker will show up automatically.
3137 MeterMarkerDrag::aborted (bool moved)
3139 _marker->set_position (_marker->meter().frame ());
3142 TempoMap& map (_editor->session()->tempo_map());
3143 /* we removed it before, so add it back now */
3144 map.add_meter (_marker->meter(), _marker->meter().frame());
3145 // delete the dummy marker we used for visual representation while moving.
3146 // a new visual marker will show up automatically.
3151 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3155 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3157 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3162 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3164 Drag::start_grab (event, cursor);
3165 show_verbose_cursor_time (adjusted_current_frame (event));
3169 TempoMarkerDrag::setup_pointer_frame_offset ()
3171 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3175 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3177 if (!_marker->tempo().movable()) {
3183 // create a dummy marker for visual representation of moving the
3184 // section, because whether its a copy or not, we're going to
3185 // leave or lose the original marker (leave if its a copy; lose if its
3186 // not, because we'll remove it from the map).
3188 // create a dummy marker for visual representation of moving the copy.
3189 // The actual copying is not done before we reach the finish callback.
3192 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3194 TempoSection section (_marker->tempo());
3196 _marker = new TempoMarker (
3198 *_editor->tempo_group,
3199 ARDOUR_UI::config()->color ("tempo marker"),
3201 *new TempoSection (_marker->tempo())
3204 /* use the new marker for the grab */
3205 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3208 TempoMap& map (_editor->session()->tempo_map());
3209 /* get current state */
3210 before_state = &map.get_state();
3211 /* remove the section while we drag it */
3212 map.remove_tempo (section, true);
3216 framepos_t const pf = adjusted_current_frame (event);
3217 _marker->set_position (pf);
3218 show_verbose_cursor_time (pf);
3222 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3224 if (!movement_occurred) {
3225 if (was_double_click()) {
3226 _editor->edit_tempo_marker (*_marker);
3231 if (!_marker->tempo().movable()) {
3235 motion (event, false);
3237 TempoMap& map (_editor->session()->tempo_map());
3238 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3239 Timecode::BBT_Time when;
3241 map.bbt_time (beat_time, when);
3243 if (_copy == true) {
3244 _editor->begin_reversible_command (_("copy tempo mark"));
3245 XMLNode &before = map.get_state();
3246 map.add_tempo (_marker->tempo(), when);
3247 XMLNode &after = map.get_state();
3248 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3249 _editor->commit_reversible_command ();
3252 _editor->begin_reversible_command (_("move tempo mark"));
3253 /* we removed it before, so add it back now */
3254 map.add_tempo (_marker->tempo(), when);
3255 XMLNode &after = map.get_state();
3256 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3257 _editor->commit_reversible_command ();
3260 // delete the dummy marker we used for visual representation while moving.
3261 // a new visual marker will show up automatically.
3266 TempoMarkerDrag::aborted (bool moved)
3268 _marker->set_position (_marker->tempo().frame());
3270 TempoMap& map (_editor->session()->tempo_map());
3271 /* we removed it before, so add it back now */
3272 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3273 // delete the dummy marker we used for visual representation while moving.
3274 // a new visual marker will show up automatically.
3279 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3280 : Drag (e, &c.track_canvas_item(), false)
3284 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3287 /** Do all the things we do when dragging the playhead to make it look as though
3288 * we have located, without actually doing the locate (because that would cause
3289 * the diskstream buffers to be refilled, which is too slow).
3292 CursorDrag::fake_locate (framepos_t t)
3294 _editor->playhead_cursor->set_position (t);
3296 Session* s = _editor->session ();
3297 if (s->timecode_transmission_suspended ()) {
3298 framepos_t const f = _editor->playhead_cursor->current_frame ();
3299 /* This is asynchronous so it will be sent "now"
3301 s->send_mmc_locate (f);
3302 /* These are synchronous and will be sent during the next
3305 s->queue_full_time_code ();
3306 s->queue_song_position_pointer ();
3309 show_verbose_cursor_time (t);
3310 _editor->UpdateAllTransportClocks (t);
3314 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3316 Drag::start_grab (event, c);
3317 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3319 _grab_zoom = _editor->samples_per_pixel;
3321 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3323 _editor->snap_to_with_modifier (where, event);
3325 _editor->_dragging_playhead = true;
3327 Session* s = _editor->session ();
3329 /* grab the track canvas item as well */
3331 _cursor.track_canvas_item().grab();
3334 if (_was_rolling && _stop) {
3338 if (s->is_auditioning()) {
3339 s->cancel_audition ();
3343 if (AudioEngine::instance()->connected()) {
3345 /* do this only if we're the engine is connected
3346 * because otherwise this request will never be
3347 * serviced and we'll busy wait forever. likewise,
3348 * notice if we are disconnected while waiting for the
3349 * request to be serviced.
3352 s->request_suspend_timecode_transmission ();
3353 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3354 /* twiddle our thumbs */
3359 fake_locate (where - snap_delta (event->button.state));
3363 CursorDrag::motion (GdkEvent* event, bool)
3365 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3366 _editor->snap_to_with_modifier (where, event);
3367 if (where != last_pointer_frame()) {
3368 fake_locate (where - snap_delta (event->button.state));
3373 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3375 _editor->_dragging_playhead = false;
3377 _cursor.track_canvas_item().ungrab();
3379 if (!movement_occurred && _stop) {
3383 motion (event, false);
3385 Session* s = _editor->session ();
3387 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3388 _editor->_pending_locate_request = true;
3389 s->request_resume_timecode_transmission ();
3394 CursorDrag::aborted (bool)
3396 _cursor.track_canvas_item().ungrab();
3398 if (_editor->_dragging_playhead) {
3399 _editor->session()->request_resume_timecode_transmission ();
3400 _editor->_dragging_playhead = false;
3403 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3406 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3407 : RegionDrag (e, i, p, v)
3409 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3413 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3415 Drag::start_grab (event, cursor);
3417 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3418 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3419 setup_snap_delta (r->position ());
3421 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3425 FadeInDrag::setup_pointer_frame_offset ()
3427 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3428 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3429 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3433 FadeInDrag::motion (GdkEvent* event, bool)
3435 framecnt_t fade_length;
3437 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3438 _editor->snap_to_with_modifier (pos, event);
3439 pos -= snap_delta (event->button.state);
3441 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3443 if (pos < (region->position() + 64)) {
3444 fade_length = 64; // this should be a minimum defined somewhere
3445 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3446 fade_length = region->length() - region->fade_out()->back()->when - 1;
3448 fade_length = pos - region->position();
3451 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3453 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3459 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3462 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3466 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3468 if (!movement_occurred) {
3472 framecnt_t fade_length;
3473 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3474 _editor->snap_to_with_modifier (pos, event);
3475 pos -= snap_delta (event->button.state);
3477 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3479 if (pos < (region->position() + 64)) {
3480 fade_length = 64; // this should be a minimum defined somewhere
3481 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3482 fade_length = region->length() - region->fade_out()->back()->when - 1;
3484 fade_length = pos - region->position();
3487 bool in_command = false;
3489 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3491 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3497 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3498 XMLNode &before = alist->get_state();
3500 tmp->audio_region()->set_fade_in_length (fade_length);
3501 tmp->audio_region()->set_fade_in_active (true);
3504 _editor->begin_reversible_command (_("change fade in length"));
3507 XMLNode &after = alist->get_state();
3508 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3512 _editor->commit_reversible_command ();
3517 FadeInDrag::aborted (bool)
3519 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3520 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3526 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3530 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3531 : RegionDrag (e, i, p, v)
3533 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3537 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3539 Drag::start_grab (event, cursor);
3541 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3542 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3543 setup_snap_delta (r->last_frame ());
3545 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3549 FadeOutDrag::setup_pointer_frame_offset ()
3551 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3552 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3553 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3557 FadeOutDrag::motion (GdkEvent* event, bool)
3559 framecnt_t fade_length;
3561 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3562 _editor->snap_to_with_modifier (pos, event);
3563 pos -= snap_delta (event->button.state);
3565 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3567 if (pos > (region->last_frame() - 64)) {
3568 fade_length = 64; // this should really be a minimum fade defined somewhere
3569 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3570 fade_length = region->length() - region->fade_in()->back()->when - 1;
3572 fade_length = region->last_frame() - pos;
3575 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3577 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3583 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3586 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3590 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3592 if (!movement_occurred) {
3596 framecnt_t fade_length;
3598 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3599 _editor->snap_to_with_modifier (pos, event);
3600 pos -= snap_delta (event->button.state);
3602 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3604 if (pos > (region->last_frame() - 64)) {
3605 fade_length = 64; // this should really be a minimum fade defined somewhere
3606 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3607 fade_length = region->length() - region->fade_in()->back()->when - 1;
3609 fade_length = region->last_frame() - pos;
3612 bool in_command = false;
3614 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3616 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3622 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3623 XMLNode &before = alist->get_state();
3625 tmp->audio_region()->set_fade_out_length (fade_length);
3626 tmp->audio_region()->set_fade_out_active (true);
3629 _editor->begin_reversible_command (_("change fade out length"));
3632 XMLNode &after = alist->get_state();
3633 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3637 _editor->commit_reversible_command ();
3642 FadeOutDrag::aborted (bool)
3644 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3645 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3651 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3655 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3658 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3660 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3663 _points.push_back (ArdourCanvas::Duple (0, 0));
3664 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3667 MarkerDrag::~MarkerDrag ()
3669 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3674 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3676 location = new Location (*l);
3677 markers.push_back (m);
3682 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3684 Drag::start_grab (event, cursor);
3688 Location *location = _editor->find_location_from_marker (_marker, is_start);
3689 _editor->_dragging_edit_point = true;
3691 update_item (location);
3693 // _drag_line->show();
3694 // _line->raise_to_top();
3697 show_verbose_cursor_time (location->start());
3699 show_verbose_cursor_time (location->end());
3702 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3705 case Selection::Toggle:
3706 /* we toggle on the button release */
3708 case Selection::Set:
3709 if (!_editor->selection->selected (_marker)) {
3710 _editor->selection->set (_marker);
3713 case Selection::Extend:
3715 Locations::LocationList ll;
3716 list<ArdourMarker*> to_add;
3718 _editor->selection->markers.range (s, e);
3719 s = min (_marker->position(), s);
3720 e = max (_marker->position(), e);
3723 if (e < max_framepos) {
3726 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3727 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3728 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3731 to_add.push_back (lm->start);
3734 to_add.push_back (lm->end);
3738 if (!to_add.empty()) {
3739 _editor->selection->add (to_add);
3743 case Selection::Add:
3744 _editor->selection->add (_marker);
3748 /* Set up copies for us to manipulate during the drag
3751 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3753 Location* l = _editor->find_location_from_marker (*i, is_start);
3760 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3762 /* range: check that the other end of the range isn't
3765 CopiedLocationInfo::iterator x;
3766 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3767 if (*(*x).location == *l) {
3771 if (x == _copied_locations.end()) {
3772 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3774 (*x).markers.push_back (*i);
3775 (*x).move_both = true;
3783 MarkerDrag::setup_pointer_frame_offset ()
3786 Location *location = _editor->find_location_from_marker (_marker, is_start);
3787 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3791 MarkerDrag::motion (GdkEvent* event, bool)
3793 framecnt_t f_delta = 0;
3795 bool move_both = false;
3796 Location *real_location;
3797 Location *copy_location = 0;
3799 framepos_t const newframe = adjusted_current_frame (event);
3800 framepos_t next = newframe;
3802 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
3806 CopiedLocationInfo::iterator x;
3808 /* find the marker we're dragging, and compute the delta */
3810 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3812 copy_location = (*x).location;
3814 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3816 /* this marker is represented by this
3817 * CopiedLocationMarkerInfo
3820 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3825 if (real_location->is_mark()) {
3826 f_delta = newframe - copy_location->start();
3830 switch (_marker->type()) {
3831 case ArdourMarker::SessionStart:
3832 case ArdourMarker::RangeStart:
3833 case ArdourMarker::LoopStart:
3834 case ArdourMarker::PunchIn:
3835 f_delta = newframe - copy_location->start();
3838 case ArdourMarker::SessionEnd:
3839 case ArdourMarker::RangeEnd:
3840 case ArdourMarker::LoopEnd:
3841 case ArdourMarker::PunchOut:
3842 f_delta = newframe - copy_location->end();
3845 /* what kind of marker is this ? */
3854 if (x == _copied_locations.end()) {
3855 /* hmm, impossible - we didn't find the dragged marker */
3859 /* now move them all */
3861 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3863 copy_location = x->location;
3865 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3869 if (real_location->locked()) {
3873 if (copy_location->is_mark()) {
3877 copy_location->set_start (copy_location->start() + f_delta);
3881 framepos_t new_start = copy_location->start() + f_delta;
3882 framepos_t new_end = copy_location->end() + f_delta;
3884 if (is_start) { // start-of-range marker
3886 if (move_both || (*x).move_both) {
3887 copy_location->set_start (new_start);
3888 copy_location->set_end (new_end);
3889 } else if (new_start < copy_location->end()) {
3890 copy_location->set_start (new_start);
3891 } else if (newframe > 0) {
3892 _editor->snap_to (next, RoundUpAlways, true);
3893 copy_location->set_end (next);
3894 copy_location->set_start (newframe);
3897 } else { // end marker
3899 if (move_both || (*x).move_both) {
3900 copy_location->set_end (new_end);
3901 copy_location->set_start (new_start);
3902 } else if (new_end > copy_location->start()) {
3903 copy_location->set_end (new_end);
3904 } else if (newframe > 0) {
3905 _editor->snap_to (next, RoundDownAlways, true);
3906 copy_location->set_start (next);
3907 copy_location->set_end (newframe);
3912 update_item (copy_location);
3914 /* now lookup the actual GUI items used to display this
3915 * location and move them to wherever the copy of the location
3916 * is now. This means that the logic in ARDOUR::Location is
3917 * still enforced, even though we are not (yet) modifying
3918 * the real Location itself.
3921 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3924 lm->set_position (copy_location->start(), copy_location->end());
3929 assert (!_copied_locations.empty());
3931 show_verbose_cursor_time (newframe);
3935 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3937 if (!movement_occurred) {
3939 if (was_double_click()) {
3940 _editor->rename_marker (_marker);
3944 /* just a click, do nothing but finish
3945 off the selection process
3948 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3951 case Selection::Set:
3952 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3953 _editor->selection->set (_marker);
3957 case Selection::Toggle:
3958 /* we toggle on the button release, click only */
3959 _editor->selection->toggle (_marker);
3962 case Selection::Extend:
3963 case Selection::Add:
3970 _editor->_dragging_edit_point = false;
3972 XMLNode &before = _editor->session()->locations()->get_state();
3973 bool in_command = false;
3975 MarkerSelection::iterator i;
3976 CopiedLocationInfo::iterator x;
3979 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3980 x != _copied_locations.end() && i != _editor->selection->markers.end();
3983 Location * location = _editor->find_location_from_marker (*i, is_start);
3987 if (location->locked()) {
3991 _editor->begin_reversible_command ( _("move marker") );
3994 if (location->is_mark()) {
3995 location->set_start (((*x).location)->start());
3997 location->set (((*x).location)->start(), ((*x).location)->end());
4003 XMLNode &after = _editor->session()->locations()->get_state();
4004 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4005 _editor->commit_reversible_command ();
4010 MarkerDrag::aborted (bool movement_occured)
4012 if (!movement_occured) {
4016 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4018 /* move all markers to their original location */
4021 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4024 Location * location = _editor->find_location_from_marker (*m, is_start);
4027 (*m)->set_position (is_start ? location->start() : location->end());
4034 MarkerDrag::update_item (Location*)
4039 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4041 _cumulative_x_drag (0),
4042 _cumulative_y_drag (0)
4044 if (_zero_gain_fraction < 0.0) {
4045 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4048 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4050 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4056 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4058 Drag::start_grab (event, _editor->cursors()->fader);
4060 // start the grab at the center of the control point so
4061 // the point doesn't 'jump' to the mouse after the first drag
4062 _fixed_grab_x = _point->get_x();
4063 _fixed_grab_y = _point->get_y();
4065 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4066 setup_snap_delta (pos);
4068 float const fraction = 1 - (_point->get_y() / _point->line().height());
4069 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4071 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4073 if (!_point->can_slide ()) {
4074 _x_constrained = true;
4079 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4081 double dx = _drags->current_pointer_x() - last_pointer_x();
4082 double dy = current_pointer_y() - last_pointer_y();
4084 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4089 /* coordinate in pixels relative to the start of the region (for region-based automation)
4090 or track (for track-based automation) */
4091 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4092 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4094 // calculate zero crossing point. back off by .01 to stay on the
4095 // positive side of zero
4096 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4098 if (_x_constrained) {
4101 if (_y_constrained) {
4105 _cumulative_x_drag = cx - _fixed_grab_x;
4106 _cumulative_y_drag = cy - _fixed_grab_y;
4108 // make sure we hit zero when passing through
4109 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4115 cy = min ((double) _point->line().height(), cy);
4117 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4119 if (!_x_constrained) {
4120 _editor->snap_to_with_modifier (cx_frames, event);
4123 cx_frames -= snap_delta (event->button.state);
4124 cx_frames = min (cx_frames, _point->line().maximum_time());
4126 float const fraction = 1.0 - (cy / _point->line().height());
4129 _editor->begin_reversible_command (_("automation event move"));
4130 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
4133 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4135 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4139 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4141 if (!movement_occurred) {
4144 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4145 _editor->reset_point_selection ();
4149 motion (event, false);
4150 _point->line().end_drag (_pushing, _final_index);
4151 _editor->commit_reversible_command ();
4156 ControlPointDrag::aborted (bool)
4158 _point->line().reset ();
4162 ControlPointDrag::active (Editing::MouseMode m)
4164 if (m == Editing::MouseDraw) {
4165 /* always active in mouse draw */
4169 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4170 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4173 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4176 , _cumulative_y_drag (0)
4180 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4184 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4186 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4189 _item = &_line->grab_item ();
4191 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4192 origin, and ditto for y.
4195 double cx = event->button.x;
4196 double cy = event->button.y;
4198 _line->parent_group().canvas_to_item (cx, cy);
4200 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4202 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4203 /* no adjacent points */
4207 Drag::start_grab (event, _editor->cursors()->fader);
4209 /* store grab start in parent frame */
4214 double fraction = 1.0 - (cy / _line->height());
4216 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4220 LineDrag::motion (GdkEvent* event, bool first_move)
4222 double dy = current_pointer_y() - last_pointer_y();
4224 if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4228 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4230 _cumulative_y_drag = cy - _fixed_grab_y;
4233 cy = min ((double) _line->height(), cy);
4235 double const fraction = 1.0 - (cy / _line->height());
4239 _editor->begin_reversible_command (_("automation range move"));
4240 _line->start_drag_line (_before, _after, fraction);
4243 /* we are ignoring x position for this drag, so we can just pass in anything */
4244 _line->drag_motion (0, fraction, true, false, ignored);
4246 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4250 LineDrag::finished (GdkEvent* event, bool movement_occured)
4252 if (movement_occured) {
4253 motion (event, false);
4254 _line->end_drag (false, 0);
4255 _editor->commit_reversible_command ();
4257 /* add a new control point on the line */
4259 AutomationTimeAxisView* atv;
4261 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4262 framepos_t where = _editor->window_event_sample (event, 0, 0);
4263 atv->add_automation_event (event, where, event->button.y, false);
4269 LineDrag::aborted (bool)
4274 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4277 _cumulative_x_drag (0)
4279 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4283 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4285 Drag::start_grab (event);
4287 _line = reinterpret_cast<Line*> (_item);
4290 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4292 double cx = event->button.x;
4293 double cy = event->button.y;
4295 _item->parent()->canvas_to_item (cx, cy);
4297 /* store grab start in parent frame */
4298 _region_view_grab_x = cx;
4300 _before = *(float*) _item->get_data ("position");
4302 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4304 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4308 FeatureLineDrag::motion (GdkEvent*, bool)
4310 double dx = _drags->current_pointer_x() - last_pointer_x();
4312 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4314 _cumulative_x_drag += dx;
4316 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4325 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4327 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4329 float *pos = new float;
4332 _line->set_data ("position", pos);
4338 FeatureLineDrag::finished (GdkEvent*, bool)
4340 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4341 _arv->update_transient(_before, _before);
4345 FeatureLineDrag::aborted (bool)
4350 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4352 , _vertical_only (false)
4354 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4358 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4360 Drag::start_grab (event);
4361 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4365 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4372 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4374 framepos_t grab = grab_frame ();
4375 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4376 _editor->snap_to_with_modifier (grab, event);
4378 grab = raw_grab_frame ();
4381 /* base start and end on initial click position */
4391 if (current_pointer_y() < grab_y()) {
4392 y1 = current_pointer_y();
4395 y2 = current_pointer_y();
4399 if (start != end || y1 != y2) {
4401 double x1 = _editor->sample_to_pixel (start);
4402 double x2 = _editor->sample_to_pixel (end);
4403 const double min_dimension = 2.0;
4405 if (_vertical_only) {
4406 /* fixed 10 pixel width */
4410 x2 = min (x1 - min_dimension, x2);
4412 x2 = max (x1 + min_dimension, x2);
4417 y2 = min (y1 - min_dimension, y2);
4419 y2 = max (y1 + min_dimension, y2);
4422 /* translate rect into item space and set */
4424 ArdourCanvas::Rect r (x1, y1, x2, y2);
4426 /* this drag is a _trackview_only == true drag, so the y1 and
4427 * y2 (computed using current_pointer_y() and grab_y()) will be
4428 * relative to the top of the trackview group). The
4429 * rubberband rect has the same parent/scroll offset as the
4430 * the trackview group, so we can use the "r" rect directly
4431 * to set the shape of the rubberband.
4434 _editor->rubberband_rect->set (r);
4435 _editor->rubberband_rect->show();
4436 _editor->rubberband_rect->raise_to_top();
4438 show_verbose_cursor_time (pf);
4440 do_select_things (event, true);
4445 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4449 framepos_t grab = grab_frame ();
4450 framepos_t lpf = last_pointer_frame ();
4452 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4453 grab = raw_grab_frame ();
4454 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4468 if (current_pointer_y() < grab_y()) {
4469 y1 = current_pointer_y();
4472 y2 = current_pointer_y();
4476 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4480 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4482 if (movement_occurred) {
4484 motion (event, false);
4485 do_select_things (event, false);
4491 bool do_deselect = true;
4492 MidiTimeAxisView* mtv;
4494 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4496 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4497 /* nothing selected */
4498 add_midi_region (mtv);
4499 do_deselect = false;
4503 /* do not deselect if Primary or Tertiary (toggle-select or
4504 * extend-select are pressed.
4507 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4508 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4515 _editor->rubberband_rect->hide();
4519 RubberbandSelectDrag::aborted (bool)
4521 _editor->rubberband_rect->hide ();
4524 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4525 : RegionDrag (e, i, p, v)
4527 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4531 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4533 Drag::start_grab (event, cursor);
4535 _editor->get_selection().add (_primary);
4537 framepos_t where = _primary->region()->position();
4538 setup_snap_delta (where);
4540 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4544 TimeFXDrag::motion (GdkEvent* event, bool)
4546 RegionView* rv = _primary;
4547 StreamView* cv = rv->get_time_axis_view().view ();
4549 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4550 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4551 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4552 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4553 _editor->snap_to_with_modifier (pf, event);
4554 pf -= snap_delta (event->button.state);
4556 if (pf > rv->region()->position()) {
4557 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4560 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4564 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4566 _primary->get_time_axis_view().hide_timestretch ();
4568 if (!movement_occurred) {
4572 if (last_pointer_frame() < _primary->region()->position()) {
4573 /* backwards drag of the left edge - not usable */
4577 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4579 float percentage = (double) newlen / (double) _primary->region()->length();
4581 #ifndef USE_RUBBERBAND
4582 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4583 if (_primary->region()->data_type() == DataType::AUDIO) {
4584 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4588 if (!_editor->get_selection().regions.empty()) {
4589 /* primary will already be included in the selection, and edit
4590 group shared editing will propagate selection across
4591 equivalent regions, so just use the current region
4595 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4596 error << _("An error occurred while executing time stretch operation") << endmsg;
4602 TimeFXDrag::aborted (bool)
4604 _primary->get_time_axis_view().hide_timestretch ();
4607 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4610 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4614 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4616 Drag::start_grab (event);
4620 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4622 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4626 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4628 if (movement_occurred && _editor->session()) {
4629 /* make sure we stop */
4630 _editor->session()->request_transport_speed (0.0);
4635 ScrubDrag::aborted (bool)
4640 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4644 , _time_selection_at_start (!_editor->get_selection().time.empty())
4646 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4648 if (_time_selection_at_start) {
4649 start_at_start = _editor->get_selection().time.start();
4650 end_at_start = _editor->get_selection().time.end_frame();
4655 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4657 if (_editor->session() == 0) {
4661 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4663 switch (_operation) {
4664 case CreateSelection:
4665 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4670 cursor = _editor->cursors()->selector;
4671 Drag::start_grab (event, cursor);
4674 case SelectionStartTrim:
4675 if (_editor->clicked_axisview) {
4676 _editor->clicked_axisview->order_selection_trims (_item, true);
4678 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4681 case SelectionEndTrim:
4682 if (_editor->clicked_axisview) {
4683 _editor->clicked_axisview->order_selection_trims (_item, false);
4685 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4689 Drag::start_grab (event, cursor);
4692 case SelectionExtend:
4693 Drag::start_grab (event, cursor);
4697 if (_operation == SelectionMove) {
4698 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4700 show_verbose_cursor_time (adjusted_current_frame (event));
4705 SelectionDrag::setup_pointer_frame_offset ()
4707 switch (_operation) {
4708 case CreateSelection:
4709 _pointer_frame_offset = 0;
4712 case SelectionStartTrim:
4714 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4717 case SelectionEndTrim:
4718 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4721 case SelectionExtend:
4727 SelectionDrag::motion (GdkEvent* event, bool first_move)
4729 framepos_t start = 0;
4731 framecnt_t length = 0;
4732 framecnt_t distance = 0;
4734 framepos_t const pending_position = adjusted_current_frame (event);
4736 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4740 switch (_operation) {
4741 case CreateSelection:
4743 framepos_t grab = grab_frame ();
4746 grab = adjusted_current_frame (event, false);
4747 if (grab < pending_position) {
4748 _editor->snap_to (grab, RoundDownMaybe);
4750 _editor->snap_to (grab, RoundUpMaybe);
4754 if (pending_position < grab) {
4755 start = pending_position;
4758 end = pending_position;
4762 /* first drag: Either add to the selection
4763 or create a new selection
4770 /* adding to the selection */
4771 _editor->set_selected_track_as_side_effect (Selection::Add);
4772 _editor->clicked_selection = _editor->selection->add (start, end);
4779 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4780 _editor->set_selected_track_as_side_effect (Selection::Set);
4783 _editor->clicked_selection = _editor->selection->set (start, end);
4787 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4788 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4789 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4791 _editor->selection->add (atest);
4795 /* select all tracks within the rectangle that we've marked out so far */
4796 TrackViewList new_selection;
4797 TrackViewList& all_tracks (_editor->track_views);
4799 ArdourCanvas::Coord const top = grab_y();
4800 ArdourCanvas::Coord const bottom = current_pointer_y();
4802 if (top >= 0 && bottom >= 0) {
4804 //first, find the tracks that are covered in the y range selection
4805 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4806 if ((*i)->covered_by_y_range (top, bottom)) {
4807 new_selection.push_back (*i);
4811 //now find any tracks that are GROUPED with the tracks we selected
4812 TrackViewList grouped_add = new_selection;
4813 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4814 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4815 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4816 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4817 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4818 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4819 grouped_add.push_back (*j);
4824 //now compare our list with the current selection, and add or remove as necessary
4825 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4826 TrackViewList tracks_to_add;
4827 TrackViewList tracks_to_remove;
4828 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4829 if ( !_editor->selection->tracks.contains ( *i ) )
4830 tracks_to_add.push_back ( *i );
4831 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4832 if ( !grouped_add.contains ( *i ) )
4833 tracks_to_remove.push_back ( *i );
4834 _editor->selection->add(tracks_to_add);
4835 _editor->selection->remove(tracks_to_remove);
4841 case SelectionStartTrim:
4843 start = _editor->selection->time[_editor->clicked_selection].start;
4844 end = _editor->selection->time[_editor->clicked_selection].end;
4846 if (pending_position > end) {
4849 start = pending_position;
4853 case SelectionEndTrim:
4855 start = _editor->selection->time[_editor->clicked_selection].start;
4856 end = _editor->selection->time[_editor->clicked_selection].end;
4858 if (pending_position < start) {
4861 end = pending_position;
4868 start = _editor->selection->time[_editor->clicked_selection].start;
4869 end = _editor->selection->time[_editor->clicked_selection].end;
4871 length = end - start;
4872 distance = pending_position - start;
4873 start = pending_position;
4874 _editor->snap_to (start);
4876 end = start + length;
4880 case SelectionExtend:
4885 switch (_operation) {
4887 if (_time_selection_at_start) {
4888 _editor->selection->move_time (distance);
4892 _editor->selection->replace (_editor->clicked_selection, start, end);
4896 if (_operation == SelectionMove) {
4897 show_verbose_cursor_time(start);
4899 show_verbose_cursor_time(pending_position);
4904 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4906 Session* s = _editor->session();
4908 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4909 if (movement_occurred) {
4910 motion (event, false);
4911 /* XXX this is not object-oriented programming at all. ick */
4912 if (_editor->selection->time.consolidate()) {
4913 _editor->selection->TimeChanged ();
4916 /* XXX what if its a music time selection? */
4918 if (s->get_play_range() && s->transport_rolling()) {
4919 s->request_play_range (&_editor->selection->time, true);
4921 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4922 if (_operation == SelectionEndTrim)
4923 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4925 s->request_locate (_editor->get_selection().time.start());
4929 if (_editor->get_selection().time.length() != 0) {
4930 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
4932 s->clear_range_selection ();
4937 /* just a click, no pointer movement.
4940 if (_operation == SelectionExtend) {
4941 if (_time_selection_at_start) {
4942 framepos_t pos = adjusted_current_frame (event, false);
4943 framepos_t start = min (pos, start_at_start);
4944 framepos_t end = max (pos, end_at_start);
4945 _editor->selection->set (start, end);
4948 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4949 if (_editor->clicked_selection) {
4950 _editor->selection->remove (_editor->clicked_selection);
4953 if (!_editor->clicked_selection) {
4954 _editor->selection->clear_time();
4959 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4960 _editor->selection->set (_editor->clicked_axisview);
4963 if (s && s->get_play_range () && s->transport_rolling()) {
4964 s->request_stop (false, false);
4969 _editor->stop_canvas_autoscroll ();
4970 _editor->clicked_selection = 0;
4971 _editor->commit_reversible_selection_op ();
4975 SelectionDrag::aborted (bool)
4980 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4981 : Drag (e, i, false),
4985 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4987 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4988 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4989 physical_screen_height (_editor->get_window())));
4990 _drag_rect->hide ();
4992 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4993 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4996 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4998 /* normal canvas items will be cleaned up when their parent group is deleted. But
4999 this item is created as the child of a long-lived parent group, and so we
5000 need to explicitly delete it.
5006 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5008 if (_editor->session() == 0) {
5012 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5014 if (!_editor->temp_location) {
5015 _editor->temp_location = new Location (*_editor->session());
5018 switch (_operation) {
5019 case CreateSkipMarker:
5020 case CreateRangeMarker:
5021 case CreateTransportMarker:
5022 case CreateCDMarker:
5024 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5029 cursor = _editor->cursors()->selector;
5033 Drag::start_grab (event, cursor);
5035 show_verbose_cursor_time (adjusted_current_frame (event));
5039 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5041 framepos_t start = 0;
5043 ArdourCanvas::Rectangle *crect;
5045 switch (_operation) {
5046 case CreateSkipMarker:
5047 crect = _editor->range_bar_drag_rect;
5049 case CreateRangeMarker:
5050 crect = _editor->range_bar_drag_rect;
5052 case CreateTransportMarker:
5053 crect = _editor->transport_bar_drag_rect;
5055 case CreateCDMarker:
5056 crect = _editor->cd_marker_bar_drag_rect;
5059 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5064 framepos_t const pf = adjusted_current_frame (event);
5066 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5067 framepos_t grab = grab_frame ();
5068 _editor->snap_to (grab);
5070 if (pf < grab_frame()) {
5078 /* first drag: Either add to the selection
5079 or create a new selection.
5084 _editor->temp_location->set (start, end);
5088 update_item (_editor->temp_location);
5090 //_drag_rect->raise_to_top();
5096 _editor->temp_location->set (start, end);
5098 double x1 = _editor->sample_to_pixel (start);
5099 double x2 = _editor->sample_to_pixel (end);
5103 update_item (_editor->temp_location);
5106 show_verbose_cursor_time (pf);
5111 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5113 Location * newloc = 0;
5117 if (movement_occurred) {
5118 motion (event, false);
5121 switch (_operation) {
5122 case CreateSkipMarker:
5123 case CreateRangeMarker:
5124 case CreateCDMarker:
5126 XMLNode &before = _editor->session()->locations()->get_state();
5127 if (_operation == CreateSkipMarker) {
5128 _editor->begin_reversible_command (_("new skip marker"));
5129 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5130 flags = Location::IsRangeMarker | Location::IsSkip;
5131 _editor->range_bar_drag_rect->hide();
5132 } else if (_operation == CreateCDMarker) {
5133 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5134 _editor->begin_reversible_command (_("new CD marker"));
5135 flags = Location::IsRangeMarker | Location::IsCDMarker;
5136 _editor->cd_marker_bar_drag_rect->hide();
5138 _editor->begin_reversible_command (_("new skip marker"));
5139 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5140 flags = Location::IsRangeMarker;
5141 _editor->range_bar_drag_rect->hide();
5143 newloc = new Location (
5144 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5147 _editor->session()->locations()->add (newloc, true);
5148 XMLNode &after = _editor->session()->locations()->get_state();
5149 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5150 _editor->commit_reversible_command ();
5154 case CreateTransportMarker:
5155 // popup menu to pick loop or punch
5156 _editor->new_transport_marker_context_menu (&event->button, _item);
5162 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5164 if (_operation == CreateTransportMarker) {
5166 /* didn't drag, so just locate */
5168 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5170 } else if (_operation == CreateCDMarker) {
5172 /* didn't drag, but mark is already created so do
5175 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5180 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5182 if (end == max_framepos) {
5183 end = _editor->session()->current_end_frame ();
5186 if (start == max_framepos) {
5187 start = _editor->session()->current_start_frame ();
5190 switch (_editor->mouse_mode) {
5192 /* find the two markers on either side and then make the selection from it */
5193 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5197 /* find the two markers on either side of the click and make the range out of it */
5198 _editor->selection->set (start, end);
5207 _editor->stop_canvas_autoscroll ();
5211 RangeMarkerBarDrag::aborted (bool movement_occured)
5213 if (movement_occured) {
5214 _drag_rect->hide ();
5219 RangeMarkerBarDrag::update_item (Location* location)
5221 double const x1 = _editor->sample_to_pixel (location->start());
5222 double const x2 = _editor->sample_to_pixel (location->end());
5224 _drag_rect->set_x0 (x1);
5225 _drag_rect->set_x1 (x2);
5228 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5230 , _cumulative_dx (0)
5231 , _cumulative_dy (0)
5233 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5235 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5237 _region = &_primary->region_view ();
5238 _note_height = _region->midi_stream_view()->note_height ();
5242 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5244 Drag::start_grab (event);
5245 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5247 if (!(_was_selected = _primary->selected())) {
5249 /* tertiary-click means extend selection - we'll do that on button release,
5250 so don't add it here, because otherwise we make it hard to figure
5251 out the "extend-to" range.
5254 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5257 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5260 _region->note_selected (_primary, true);
5262 _region->unique_select (_primary);
5265 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5266 _editor->commit_reversible_selection_op();
5271 /** @return Current total drag x change in frames */
5273 NoteDrag::total_dx (const guint state) const
5276 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5278 /* primary note time */
5279 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5281 /* new time of the primary note in session frames */
5282 frameoffset_t st = n + dx + snap_delta (state);
5284 framepos_t const rp = _region->region()->position ();
5286 /* prevent the note being dragged earlier than the region's position */
5289 /* possibly snap and return corresponding delta */
5293 if (ArdourKeyboard::indicates_snap (state)) {
5294 if (_editor->snap_mode () != SnapOff) {
5298 if (_editor->snap_mode () == SnapOff) {
5300 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5301 if (ArdourKeyboard::indicates_snap_delta (state)) {
5309 ret = _region->snap_frame_to_frame (st - rp) + rp - n - snap_delta (state);
5311 ret = st - n - snap_delta (state);
5316 /** @return Current total drag y change in note number */
5318 NoteDrag::total_dy () const
5320 MidiStreamView* msv = _region->midi_stream_view ();
5321 double const y = _region->midi_view()->y_position ();
5322 /* new current note */
5323 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5325 n = max (msv->lowest_note(), n);
5326 n = min (msv->highest_note(), n);
5327 /* and work out delta */
5328 return n - msv->y_to_note (grab_y() - y);
5332 NoteDrag::motion (GdkEvent * event, bool)
5334 /* Total change in x and y since the start of the drag */
5335 frameoffset_t const dx = total_dx (event->button.state);
5336 int8_t const dy = total_dy ();
5338 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5339 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5340 double const tdy = -dy * _note_height - _cumulative_dy;
5343 _cumulative_dx += tdx;
5344 _cumulative_dy += tdy;
5346 int8_t note_delta = total_dy();
5348 _region->move_selection (tdx, tdy, note_delta);
5350 /* the new note value may be the same as the old one, but we
5351 * don't know what that means because the selection may have
5352 * involved more than one note and we might be doing something
5353 * odd with them. so show the note value anyway, always.
5357 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5359 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5360 (int) floor ((double)new_note));
5362 show_verbose_cursor_text (buf);
5367 NoteDrag::finished (GdkEvent* ev, bool moved)
5370 /* no motion - select note */
5372 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5373 _editor->current_mouse_mode() == Editing::MouseDraw) {
5375 bool changed = false;
5377 if (_was_selected) {
5378 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5380 _region->note_deselected (_primary);
5384 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5385 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5387 if (!extend && !add && _region->selection_size() > 1) {
5388 _region->unique_select (_primary);
5390 } else if (extend) {
5391 _region->note_selected (_primary, true, true);
5394 /* it was added during button press */
5399 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5400 _editor->commit_reversible_selection_op();
5404 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5409 NoteDrag::aborted (bool)
5414 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5415 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5416 : Drag (editor, atv->base_item ())
5418 , _y_origin (atv->y_position())
5419 , _nothing_to_drag (false)
5421 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5422 setup (atv->lines ());
5425 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5426 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5427 : Drag (editor, rv->get_canvas_group ())
5429 , _y_origin (rv->get_time_axis_view().y_position())
5430 , _nothing_to_drag (false)
5433 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5435 list<boost::shared_ptr<AutomationLine> > lines;
5437 AudioRegionView* audio_view;
5438 AutomationRegionView* automation_view;
5439 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5440 lines.push_back (audio_view->get_gain_line ());
5441 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5442 lines.push_back (automation_view->line ());
5445 error << _("Automation range drag created for invalid region type") << endmsg;
5451 /** @param lines AutomationLines to drag.
5452 * @param offset Offset from the session start to the points in the AutomationLines.
5455 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5457 /* find the lines that overlap the ranges being dragged */
5458 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5459 while (i != lines.end ()) {
5460 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5463 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5465 /* check this range against all the AudioRanges that we are using */
5466 list<AudioRange>::const_iterator k = _ranges.begin ();
5467 while (k != _ranges.end()) {
5468 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5474 /* add it to our list if it overlaps at all */
5475 if (k != _ranges.end()) {
5480 _lines.push_back (n);
5486 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5490 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5492 return 1.0 - ((global_y - _y_origin) / line->height());
5496 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5498 const double v = list->eval(x);
5499 return _integral ? rint(v) : v;
5503 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5505 Drag::start_grab (event, cursor);
5507 /* Get line states before we start changing things */
5508 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5509 i->state = &i->line->get_state ();
5510 i->original_fraction = y_fraction (i->line, current_pointer_y());
5513 if (_ranges.empty()) {
5515 /* No selected time ranges: drag all points */
5516 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5517 uint32_t const N = i->line->npoints ();
5518 for (uint32_t j = 0; j < N; ++j) {
5519 i->points.push_back (i->line->nth (j));
5525 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5527 framecnt_t const half = (i->start + i->end) / 2;
5529 /* find the line that this audio range starts in */
5530 list<Line>::iterator j = _lines.begin();
5531 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5535 if (j != _lines.end()) {
5536 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5538 /* j is the line that this audio range starts in; fade into it;
5539 64 samples length plucked out of thin air.
5542 framepos_t a = i->start + 64;
5547 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5548 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5550 the_list->editor_add (p, value (the_list, p), false);
5551 the_list->editor_add (q, value (the_list, q), false);
5554 /* same thing for the end */
5557 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5561 if (j != _lines.end()) {
5562 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5564 /* j is the line that this audio range starts in; fade out of it;
5565 64 samples length plucked out of thin air.
5568 framepos_t b = i->end - 64;
5573 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5574 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5576 the_list->editor_add (p, value (the_list, p), false);
5577 the_list->editor_add (q, value (the_list, q), false);
5581 _nothing_to_drag = true;
5583 /* Find all the points that should be dragged and put them in the relevant
5584 points lists in the Line structs.
5587 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5589 uint32_t const N = i->line->npoints ();
5590 for (uint32_t j = 0; j < N; ++j) {
5592 /* here's a control point on this line */
5593 ControlPoint* p = i->line->nth (j);
5594 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5596 /* see if it's inside a range */
5597 list<AudioRange>::const_iterator k = _ranges.begin ();
5598 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5602 if (k != _ranges.end()) {
5603 /* dragging this point */
5604 _nothing_to_drag = false;
5605 i->points.push_back (p);
5611 if (_nothing_to_drag) {
5617 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5619 if (_nothing_to_drag) {
5624 _editor->begin_reversible_command (_("automation range move"));
5625 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5626 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5630 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5631 float const f = y_fraction (l->line, current_pointer_y());
5632 /* we are ignoring x position for this drag, so we can just pass in anything */
5634 l->line->drag_motion (0, f, true, false, ignored);
5635 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5640 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5642 if (_nothing_to_drag || !motion_occurred) {
5646 motion (event, false);
5647 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5648 i->line->end_drag (false, 0);
5651 _editor->commit_reversible_command ();
5655 AutomationRangeDrag::aborted (bool)
5657 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5662 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5664 , initial_time_axis_view (itav)
5666 /* note that time_axis_view may be null if the regionview was created
5667 * as part of a copy operation.
5669 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5670 layer = v->region()->layer ();
5671 initial_y = v->get_canvas_group()->position().y;
5672 initial_playlist = v->region()->playlist ();
5673 initial_position = v->region()->position ();
5674 initial_end = v->region()->position () + v->region()->length ();
5677 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5678 : Drag (e, i->canvas_item ())
5681 , _cumulative_dx (0)
5683 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5684 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5689 PatchChangeDrag::motion (GdkEvent* ev, bool)
5691 framepos_t f = adjusted_current_frame (ev);
5692 boost::shared_ptr<Region> r = _region_view->region ();
5693 f = max (f, r->position ());
5694 f = min (f, r->last_frame ());
5696 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5697 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5698 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5699 _cumulative_dx = dxu;
5703 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5705 if (!movement_occurred) {
5709 boost::shared_ptr<Region> r (_region_view->region ());
5710 framepos_t f = adjusted_current_frame (ev);
5711 f = max (f, r->position ());
5712 f = min (f, r->last_frame ());
5714 _region_view->move_patch_change (
5716 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5721 PatchChangeDrag::aborted (bool)
5723 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5727 PatchChangeDrag::setup_pointer_frame_offset ()
5729 boost::shared_ptr<Region> region = _region_view->region ();
5730 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5733 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5734 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5741 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5743 _region_view->update_drag_selection (
5745 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5749 MidiRubberbandSelectDrag::deselect_things ()
5754 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5755 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5758 _vertical_only = true;
5762 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5764 double const y = _region_view->midi_view()->y_position ();
5766 y1 = max (0.0, y1 - y);
5767 y2 = max (0.0, y2 - y);
5769 _region_view->update_vertical_drag_selection (
5772 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5777 MidiVerticalSelectDrag::deselect_things ()
5782 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5783 : RubberbandSelectDrag (e, i)
5789 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5791 if (drag_in_progress) {
5792 /* We just want to select things at the end of the drag, not during it */
5796 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5798 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5800 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5802 _editor->commit_reversible_selection_op ();
5806 EditorRubberbandSelectDrag::deselect_things ()
5808 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5810 _editor->selection->clear_tracks();
5811 _editor->selection->clear_regions();
5812 _editor->selection->clear_points ();
5813 _editor->selection->clear_lines ();
5814 _editor->selection->clear_midi_notes ();
5816 _editor->commit_reversible_selection_op();
5819 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5824 _note[0] = _note[1] = 0;
5827 NoteCreateDrag::~NoteCreateDrag ()
5833 NoteCreateDrag::grid_frames (framepos_t t) const
5836 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5838 grid_beats = Evoral::Beats(1);
5841 return _region_view->region_beats_to_region_frames (grid_beats);
5845 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5847 Drag::start_grab (event, cursor);
5849 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5851 framepos_t pf = _drags->current_pointer_frame ();
5852 framecnt_t const g = grid_frames (pf);
5854 /* Hack so that we always snap to the note that we are over, instead of snapping
5855 to the next one if we're more than halfway through the one we're over.
5857 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5861 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5862 _note[1] = _note[0];
5864 MidiStreamView* sv = _region_view->midi_stream_view ();
5865 double const x = _editor->sample_to_pixel (_note[0]);
5866 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5868 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5869 _drag_rect->set_outline_all ();
5870 _drag_rect->set_outline_color (0xffffff99);
5871 _drag_rect->set_fill_color (0xffffff66);
5875 NoteCreateDrag::motion (GdkEvent* event, bool)
5877 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5878 double const x0 = _editor->sample_to_pixel (_note[0]);
5879 double const x1 = _editor->sample_to_pixel (_note[1]);
5880 _drag_rect->set_x0 (std::min(x0, x1));
5881 _drag_rect->set_x1 (std::max(x0, x1));
5885 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5887 if (!had_movement) {
5891 framepos_t const start = min (_note[0], _note[1]);
5892 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5894 framecnt_t const g = grid_frames (start);
5895 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5897 if (_editor->snap_mode() == SnapNormal && length < g) {
5901 Evoral::Beats length_beats = max (
5902 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5904 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5908 NoteCreateDrag::y_to_region (double y) const
5911 _region_view->get_canvas_group()->canvas_to_item (x, y);
5916 NoteCreateDrag::aborted (bool)
5921 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5926 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5930 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5932 Drag::start_grab (event, cursor);
5936 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5942 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5945 distance = _drags->current_pointer_x() - grab_x();
5946 len = ar->fade_in()->back()->when;
5948 distance = grab_x() - _drags->current_pointer_x();
5949 len = ar->fade_out()->back()->when;
5952 /* how long should it be ? */
5954 new_length = len + _editor->pixel_to_sample (distance);
5956 /* now check with the region that this is legal */
5958 new_length = ar->verify_xfade_bounds (new_length, start);
5961 arv->reset_fade_in_shape_width (ar, new_length);
5963 arv->reset_fade_out_shape_width (ar, new_length);
5968 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5974 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5977 distance = _drags->current_pointer_x() - grab_x();
5978 len = ar->fade_in()->back()->when;
5980 distance = grab_x() - _drags->current_pointer_x();
5981 len = ar->fade_out()->back()->when;
5984 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5986 _editor->begin_reversible_command ("xfade trim");
5987 ar->playlist()->clear_owned_changes ();
5990 ar->set_fade_in_length (new_length);
5992 ar->set_fade_out_length (new_length);
5995 /* Adjusting the xfade may affect other regions in the playlist, so we need
5996 to get undo Commands from the whole playlist rather than just the
6000 vector<Command*> cmds;
6001 ar->playlist()->rdiff (cmds);
6002 _editor->session()->add_commands (cmds);
6003 _editor->commit_reversible_command ();
6008 CrossfadeEdgeDrag::aborted (bool)
6011 // arv->redraw_start_xfade ();
6013 // arv->redraw_end_xfade ();
6017 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6018 : Drag (e, item, true)
6019 , line (new EditorCursor (*e))
6021 line->set_position (pos);
6025 RegionCutDrag::~RegionCutDrag ()
6031 RegionCutDrag::motion (GdkEvent*, bool)
6033 framepos_t where = _drags->current_pointer_frame();
6034 _editor->snap_to (where);
6036 line->set_position (where);
6040 RegionCutDrag::finished (GdkEvent*, bool)
6042 _editor->get_track_canvas()->canvas()->re_enter();
6044 framepos_t pos = _drags->current_pointer_frame();
6048 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6054 _editor->split_regions_at (pos, rs);
6058 RegionCutDrag::aborted (bool)