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 "midi_region_view.h"
51 #include "ardour_ui.h"
52 #include "gui_thread.h"
53 #include "control_point.h"
54 #include "region_gain_line.h"
55 #include "editor_drag.h"
56 #include "audio_time_axis.h"
57 #include "midi_time_axis.h"
58 #include "selection.h"
59 #include "midi_selection.h"
60 #include "automation_time_axis.h"
62 #include "editor_cursors.h"
63 #include "mouse_cursors.h"
64 #include "note_base.h"
65 #include "patch_change.h"
66 #include "verbose_cursor.h"
69 using namespace ARDOUR;
72 using namespace Gtkmm2ext;
73 using namespace Editing;
74 using namespace ArdourCanvas;
76 using Gtkmm2ext::Keyboard;
78 double ControlPointDrag::_zero_gain_fraction = -1.0;
80 DragManager::DragManager (Editor* e)
83 , _current_pointer_frame (0)
87 DragManager::~DragManager ()
92 /** Call abort for each active drag */
98 cerr << "Aborting drag\n";
100 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
105 if (!_drags.empty ()) {
106 _editor->set_follow_playhead (_old_follow_playhead, false);
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 , _trackview_only (trackview_only)
220 , _move_threshold_passed (false)
221 , _was_double_click (false)
222 , _raw_grab_frame (0)
224 , _last_pointer_frame (0)
230 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
243 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
245 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
247 if (Keyboard::is_button2_event (&event->button)) {
248 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
249 _y_constrained = true;
250 _x_constrained = false;
252 _y_constrained = false;
253 _x_constrained = true;
256 _x_constrained = false;
257 _y_constrained = false;
260 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
261 setup_pointer_frame_offset ();
262 _grab_frame = adjusted_frame (_raw_grab_frame, event);
263 _last_pointer_frame = _grab_frame;
264 _last_pointer_x = _grab_x;
266 if (_trackview_only) {
267 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
270 _last_pointer_y = _grab_y;
275 /* CAIROCANVAS need a variant here that passes *cursor */
277 _editor->push_canvas_cursor (cursor);
280 if (_editor->session() && _editor->session()->transport_rolling()) {
283 _was_rolling = false;
286 switch (_editor->snap_type()) {
287 case SnapToRegionStart:
288 case SnapToRegionEnd:
289 case SnapToRegionSync:
290 case SnapToRegionBoundary:
291 _editor->build_region_boundary_cache ();
298 /** Call to end a drag `successfully'. Ungrabs item and calls
299 * subclass' finished() method.
301 * @param event GDK event, or 0.
302 * @return true if some movement occurred, otherwise false.
305 Drag::end_grab (GdkEvent* event)
307 _editor->stop_canvas_autoscroll ();
311 finished (event, _move_threshold_passed);
313 _editor->verbose_cursor()->hide ();
314 _editor->pop_canvas_cursor ();
316 return _move_threshold_passed;
320 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
324 if (f > _pointer_frame_offset) {
325 pos = f - _pointer_frame_offset;
329 _editor->snap_to_with_modifier (pos, event);
336 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
338 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
342 Drag::current_pointer_y () const
344 if (!_trackview_only) {
345 return _drags->current_pointer_y ();
348 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
352 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
354 /* check to see if we have moved in any way that matters since the last motion event */
355 if (_move_threshold_passed &&
356 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
357 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
361 pair<framecnt_t, int> const threshold = move_threshold ();
363 bool const old_move_threshold_passed = _move_threshold_passed;
365 if (!from_autoscroll && !_move_threshold_passed) {
367 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
368 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
370 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
373 if (active (_editor->mouse_mode) && _move_threshold_passed) {
375 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
376 if (!from_autoscroll) {
377 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
380 if (!_editor->autoscroll_active() || from_autoscroll) {
381 motion (event, _move_threshold_passed != old_move_threshold_passed);
383 _last_pointer_x = _drags->current_pointer_x ();
384 _last_pointer_y = current_pointer_y ();
385 _last_pointer_frame = adjusted_current_frame (event);
395 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
403 aborted (_move_threshold_passed);
405 _editor->stop_canvas_autoscroll ();
406 _editor->verbose_cursor()->hide ();
410 Drag::show_verbose_cursor_time (framepos_t frame)
412 _editor->verbose_cursor()->set_time (frame);
413 _editor->verbose_cursor()->show ();
417 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
419 _editor->verbose_cursor()->set_duration (start, end);
420 _editor->verbose_cursor()->show ();
424 Drag::show_verbose_cursor_text (string const & text)
426 _editor->verbose_cursor()->set (text);
427 _editor->verbose_cursor()->show ();
430 boost::shared_ptr<Region>
431 Drag::add_midi_region (MidiTimeAxisView* view)
433 if (_editor->session()) {
434 const TempoMap& map (_editor->session()->tempo_map());
435 framecnt_t pos = grab_frame();
436 const Meter& m = map.meter_at (pos);
437 /* not that the frame rate used here can be affected by pull up/down which
440 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
441 return view->add_region (grab_frame(), len, true);
444 return boost::shared_ptr<Region>();
447 struct EditorOrderTimeAxisViewSorter {
448 bool operator() (TimeAxisView* a, TimeAxisView* b) {
449 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
450 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
452 return ra->route()->order_key () < rb->route()->order_key ();
456 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
460 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
462 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
463 as some of the regions we are dragging may be on such tracks.
466 TrackViewList track_views = _editor->track_views;
467 track_views.sort (EditorOrderTimeAxisViewSorter ());
469 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
470 _time_axis_views.push_back (*i);
472 TimeAxisView::Children children_list = (*i)->get_child_list ();
473 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
474 _time_axis_views.push_back (j->get());
478 /* the list of views can be empty at this point if this is a region list-insert drag
481 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
482 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
485 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
489 RegionDrag::region_going_away (RegionView* v)
491 list<DraggingView>::iterator i = _views.begin ();
492 while (i != _views.end() && i->view != v) {
496 if (i != _views.end()) {
501 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
502 * or -1 if it is not found.
505 RegionDrag::find_time_axis_view (TimeAxisView* t) const
508 int const N = _time_axis_views.size ();
509 while (i < N && _time_axis_views[i] != t) {
520 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
521 : RegionDrag (e, i, p, v)
524 , _last_pointer_time_axis_view (0)
525 , _last_pointer_layer (0)
527 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
531 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
533 Drag::start_grab (event, cursor);
535 show_verbose_cursor_time (_last_frame_position);
537 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
539 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
540 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
545 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
547 /* compute the amount of pointer motion in frames, and where
548 the region would be if we moved it by that much.
550 *pending_region_position = adjusted_current_frame (event);
552 framepos_t sync_frame;
553 framecnt_t sync_offset;
556 sync_offset = _primary->region()->sync_offset (sync_dir);
558 /* we don't handle a sync point that lies before zero.
560 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
562 sync_frame = *pending_region_position + (sync_dir*sync_offset);
564 _editor->snap_to_with_modifier (sync_frame, event);
566 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
569 *pending_region_position = _last_frame_position;
572 if (*pending_region_position > max_framepos - _primary->region()->length()) {
573 *pending_region_position = _last_frame_position;
578 /* in locked edit mode, reverse the usual meaning of _x_constrained */
579 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
581 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
583 /* x movement since last time (in pixels) */
584 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
586 /* total x movement */
587 framecnt_t total_dx = *pending_region_position;
588 if (regions_came_from_canvas()) {
589 total_dx = total_dx - grab_frame ();
592 /* check that no regions have gone off the start of the session */
593 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
594 if ((i->view->region()->position() + total_dx) < 0) {
596 *pending_region_position = _last_frame_position;
607 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
609 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
610 int const n = i->time_axis_view + delta_track;
611 if (n < 0 || n >= int (_time_axis_views.size())) {
612 /* off the top or bottom track */
616 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
617 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
618 /* not a track, or the wrong type */
622 double const l = i->layer + delta_layer;
624 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
625 mode to allow the user to place a region below another on layer 0.
627 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
628 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
629 If it has, the layers will be munged later anyway, so it's ok.
635 /* all regions being dragged are ok with this change */
640 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
642 double delta_layer = 0;
643 int delta_time_axis_view = 0;
645 assert (!_views.empty ());
647 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
649 /* Find the TimeAxisView that the pointer is now over */
650 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
651 TimeAxisView* tv = r.first;
653 if (tv && tv->view()) {
654 double layer = r.second;
656 if (first_move && tv->view()->layer_display() == Stacked) {
657 tv->view()->set_layer_display (Expanded);
660 /* Here's the current pointer position in terms of time axis view and layer */
661 int const current_pointer_time_axis_view = find_time_axis_view (tv);
662 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
664 /* Work out the change in y */
666 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
667 delta_layer = current_pointer_layer - _last_pointer_layer;
670 /* Work out the change in x */
671 framepos_t pending_region_position;
672 double const x_delta = compute_x_delta (event, &pending_region_position);
673 _last_frame_position = pending_region_position;
675 /* Verify change in y */
676 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
677 /* this y movement is not allowed, so do no y movement this time */
678 delta_time_axis_view = 0;
682 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
683 /* haven't reached next snap point, and we're not switching
684 trackviews nor layers. nothing to do.
689 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
691 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
693 RegionView* rv = i->view;
695 if (rv->region()->locked() || rv->region()->video_locked()) {
702 /* reparent the regionview into a group above all
706 ArdourCanvas::Item* rvg = rv->get_canvas_group();
707 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
708 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
709 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
710 /* move the item so that it continues to appear at the
711 same location now that its parent has changed.
713 rvg->move (rv_canvas_offset - dmg_canvas_offset);
716 /* If we have moved tracks, we'll fudge the layer delta so that the
717 region gets moved back onto layer 0 on its new track; this avoids
718 confusion when dragging regions from non-zero layers onto different
721 double this_delta_layer = delta_layer;
722 if (delta_time_axis_view != 0) {
723 this_delta_layer = - i->layer;
730 if (i->time_axis_view >= 0) {
731 track_index = i->time_axis_view + delta_time_axis_view;
733 track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
736 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
740 /* The TimeAxisView that this region is now over */
741 TimeAxisView* current_tv = _time_axis_views[track_index];
743 /* Ensure it is moved from stacked -> expanded if appropriate */
744 if (current_tv->view()->layer_display() == Stacked) {
745 current_tv->view()->set_layer_display (Expanded);
748 /* We're only allowed to go -ve in layer on Expanded views */
749 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
750 this_delta_layer = - i->layer;
754 rv->set_height (current_tv->view()->child_height ());
756 /* Update show/hidden status as the region view may have come from a hidden track,
757 or have moved to one.
759 if (current_tv->hidden ()) {
760 rv->get_canvas_group()->hide ();
762 rv->get_canvas_group()->show ();
765 /* Update the DraggingView */
766 i->time_axis_view = track_index;
767 i->layer += this_delta_layer;
770 _editor->mouse_brush_insert_region (rv, pending_region_position);
774 /* Get the y coordinate of the top of the track that this region is now over */
775 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
777 /* And adjust for the layer that it should be on */
778 StreamView* cv = current_tv->view ();
779 switch (cv->layer_display ()) {
783 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
786 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
790 /* need to get the parent of the regionview
791 * canvas group and get its position in
792 * equivalent coordinate space as the trackview
793 * we are now dragging over.
796 /* Now move the region view */
797 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->canvas_origin().y);
801 /* Only move the region into the empty dropzone at the bottom if the pointer
805 if (current_pointer_y() >= 0) {
807 Coord last_track_bottom_edge;
808 if (!_time_axis_views.empty()) {
809 TimeAxisView* last = _time_axis_views.back();
810 last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
812 last_track_bottom_edge = 0;
815 rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y);
816 i->time_axis_view = -1;
820 } /* foreach region */
822 _total_x_delta += x_delta;
824 if (x_delta != 0 && !_brushing) {
825 show_verbose_cursor_time (_last_frame_position);
828 _last_pointer_time_axis_view += delta_time_axis_view;
829 _last_pointer_layer += delta_layer;
833 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
835 if (_copy && first_move) {
837 /* duplicate the regionview(s) and region(s) */
839 list<DraggingView> new_regionviews;
841 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
843 RegionView* rv = i->view;
844 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
845 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
847 const boost::shared_ptr<const Region> original = rv->region();
848 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
849 region_copy->set_position (original->position());
853 boost::shared_ptr<AudioRegion> audioregion_copy
854 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
856 nrv = new AudioRegionView (*arv, audioregion_copy);
858 boost::shared_ptr<MidiRegion> midiregion_copy
859 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
860 nrv = new MidiRegionView (*mrv, midiregion_copy);
865 nrv->get_canvas_group()->show ();
866 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
868 /* swap _primary to the copy */
870 if (rv == _primary) {
874 /* ..and deselect the one we copied */
876 rv->set_selected (false);
879 if (!new_regionviews.empty()) {
881 /* reflect the fact that we are dragging the copies */
883 _views = new_regionviews;
885 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
889 RegionMotionDrag::motion (event, first_move);
893 RegionMotionDrag::finished (GdkEvent *, bool)
895 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
900 if ((*i)->view()->layer_display() == Expanded) {
901 (*i)->view()->set_layer_display (Stacked);
907 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
909 RegionMotionDrag::finished (ev, movement_occurred);
911 if (!movement_occurred) {
915 if (was_double_click() && !_views.empty()) {
916 DraggingView dv = _views.front();
917 dv.view->show_region_editor ();
924 /* reverse this here so that we have the correct logic to finalize
928 if (Config->get_edit_mode() == Lock) {
929 _x_constrained = !_x_constrained;
932 assert (!_views.empty ());
934 /* We might have hidden region views so that they weren't visible during the drag
935 (when they have been reparented). Now everything can be shown again, as region
936 views are back in their track parent groups.
938 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
939 i->view->get_canvas_group()->show ();
942 bool const changed_position = (_last_frame_position != _primary->region()->position());
943 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
944 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
964 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
968 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
970 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
975 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
976 list<boost::shared_ptr<AudioTrack> > audio_tracks;
977 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
978 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
980 rtav->set_height (original->current_height());
984 ChanCount one_midi_port (DataType::MIDI, 1);
985 list<boost::shared_ptr<MidiTrack> > midi_tracks;
986 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
987 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
989 rtav->set_height (original->current_height());
994 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1000 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1002 RegionSelection new_views;
1003 PlaylistSet modified_playlists;
1004 RouteTimeAxisView* new_time_axis_view = 0;
1007 /* all changes were made during motion event handlers */
1009 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1013 _editor->commit_reversible_command ();
1017 if (_x_constrained) {
1018 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1020 _editor->begin_reversible_command (Operations::region_copy);
1023 /* insert the regions into their new playlists */
1024 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1026 RouteTimeAxisView* dest_rtv = 0;
1028 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1034 if (changed_position && !_x_constrained) {
1035 where = i->view->region()->position() - drag_delta;
1037 where = i->view->region()->position();
1040 if (i->time_axis_view < 0) {
1041 if (!new_time_axis_view) {
1042 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1044 dest_rtv = new_time_axis_view;
1046 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1049 if (dest_rtv != 0) {
1050 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1051 if (new_view != 0) {
1052 new_views.push_back (new_view);
1056 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1057 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1060 list<DraggingView>::const_iterator next = i;
1066 /* If we've created new regions either by copying or moving
1067 to a new track, we want to replace the old selection with the new ones
1070 if (new_views.size() > 0) {
1071 _editor->selection->set (new_views);
1074 /* write commands for the accumulated diffs for all our modified playlists */
1075 add_stateful_diff_commands_for_playlists (modified_playlists);
1077 _editor->commit_reversible_command ();
1081 RegionMoveDrag::finished_no_copy (
1082 bool const changed_position,
1083 bool const changed_tracks,
1084 framecnt_t const drag_delta
1087 RegionSelection new_views;
1088 PlaylistSet modified_playlists;
1089 PlaylistSet frozen_playlists;
1090 set<RouteTimeAxisView*> views_to_update;
1091 RouteTimeAxisView* new_time_axis_view = 0;
1094 /* all changes were made during motion event handlers */
1095 _editor->commit_reversible_command ();
1099 if (_x_constrained) {
1100 _editor->begin_reversible_command (_("fixed time region drag"));
1102 _editor->begin_reversible_command (Operations::region_drag);
1105 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1107 RegionView* rv = i->view;
1108 RouteTimeAxisView* dest_rtv = 0;
1110 if (rv->region()->locked() || rv->region()->video_locked()) {
1115 if (i->time_axis_view < 0) {
1116 if (!new_time_axis_view) {
1117 new_time_axis_view = create_destination_time_axis (rv->region(), i->initial_time_axis_view);
1119 dest_rtv = new_time_axis_view;
1121 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1126 double const dest_layer = i->layer;
1128 views_to_update.insert (dest_rtv);
1132 if (changed_position && !_x_constrained) {
1133 where = rv->region()->position() - drag_delta;
1135 where = rv->region()->position();
1138 if (changed_tracks) {
1140 /* insert into new playlist */
1142 RegionView* new_view = insert_region_into_playlist (
1143 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1146 if (new_view == 0) {
1151 new_views.push_back (new_view);
1153 /* remove from old playlist */
1155 /* the region that used to be in the old playlist is not
1156 moved to the new one - we use a copy of it. as a result,
1157 any existing editor for the region should no longer be
1160 rv->hide_region_editor();
1163 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1167 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1169 /* this movement may result in a crossfade being modified, or a layering change,
1170 so we need to get undo data from the playlist as well as the region.
1173 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1175 playlist->clear_changes ();
1178 rv->region()->clear_changes ();
1181 motion on the same track. plonk the previously reparented region
1182 back to its original canvas group (its streamview).
1183 No need to do anything for copies as they are fake regions which will be deleted.
1186 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1187 rv->get_canvas_group()->set_y_position (i->initial_y);
1190 /* just change the model */
1191 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1192 playlist->set_layer (rv->region(), dest_layer);
1195 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1197 r = frozen_playlists.insert (playlist);
1200 playlist->freeze ();
1203 rv->region()->set_position (where);
1205 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1208 if (changed_tracks) {
1210 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1211 was selected in all of them, then removing it from a playlist will have removed all
1212 trace of it from _views (i.e. there were N regions selected, we removed 1,
1213 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1214 corresponding regionview, and _views is now empty).
1216 This could have invalidated any and all iterators into _views.
1218 The heuristic we use here is: if the region selection is empty, break out of the loop
1219 here. if the region selection is not empty, then restart the loop because we know that
1220 we must have removed at least the region(view) we've just been working on as well as any
1221 that we processed on previous iterations.
1223 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1224 we can just iterate.
1228 if (_views.empty()) {
1239 /* If we've created new regions either by copying or moving
1240 to a new track, we want to replace the old selection with the new ones
1243 if (new_views.size() > 0) {
1244 _editor->selection->set (new_views);
1247 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1251 /* write commands for the accumulated diffs for all our modified playlists */
1252 add_stateful_diff_commands_for_playlists (modified_playlists);
1254 _editor->commit_reversible_command ();
1256 /* We have futzed with the layering of canvas items on our streamviews.
1257 If any region changed layer, this will have resulted in the stream
1258 views being asked to set up their region views, and all will be well.
1259 If not, we might now have badly-ordered region views. Ask the StreamViews
1260 involved to sort themselves out, just in case.
1263 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1264 (*i)->view()->playlist_layered ((*i)->track ());
1268 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1269 * @param region Region to remove.
1270 * @param playlist playlist To remove from.
1271 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1272 * that clear_changes () is only called once per playlist.
1275 RegionMoveDrag::remove_region_from_playlist (
1276 boost::shared_ptr<Region> region,
1277 boost::shared_ptr<Playlist> playlist,
1278 PlaylistSet& modified_playlists
1281 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1284 playlist->clear_changes ();
1287 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1291 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1292 * clearing the playlist's diff history first if necessary.
1293 * @param region Region to insert.
1294 * @param dest_rtv Destination RouteTimeAxisView.
1295 * @param dest_layer Destination layer.
1296 * @param where Destination position.
1297 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1298 * that clear_changes () is only called once per playlist.
1299 * @return New RegionView, or 0 if no insert was performed.
1302 RegionMoveDrag::insert_region_into_playlist (
1303 boost::shared_ptr<Region> region,
1304 RouteTimeAxisView* dest_rtv,
1307 PlaylistSet& modified_playlists
1310 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1311 if (!dest_playlist) {
1315 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1316 _new_region_view = 0;
1317 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1319 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1320 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1322 dest_playlist->clear_changes ();
1325 dest_playlist->add_region (region, where);
1327 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1328 dest_playlist->set_layer (region, dest_layer);
1333 assert (_new_region_view);
1335 return _new_region_view;
1339 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1341 _new_region_view = rv;
1345 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1347 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1348 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1350 _editor->session()->add_command (c);
1359 RegionMoveDrag::aborted (bool movement_occurred)
1363 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1370 RegionMotionDrag::aborted (movement_occurred);
1375 RegionMotionDrag::aborted (bool)
1377 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1379 StreamView* sview = (*i)->view();
1382 if (sview->layer_display() == Expanded) {
1383 sview->set_layer_display (Stacked);
1388 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1389 RegionView* rv = i->view;
1390 TimeAxisView* tv = &(rv->get_time_axis_view ());
1391 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1393 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1394 rv->get_canvas_group()->set_y_position (0);
1396 rv->move (-_total_x_delta, 0);
1397 rv->set_height (rtv->view()->child_height ());
1401 /** @param b true to brush, otherwise false.
1402 * @param c true to make copies of the regions being moved, otherwise false.
1404 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1405 : RegionMotionDrag (e, i, p, v, b),
1408 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1411 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1412 if (rtv && rtv->is_track()) {
1413 speed = rtv->track()->speed ();
1416 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1420 RegionMoveDrag::setup_pointer_frame_offset ()
1422 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1425 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1426 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1428 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1430 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1431 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1433 _primary = v->view()->create_region_view (r, false, false);
1435 _primary->get_canvas_group()->show ();
1436 _primary->set_position (pos, 0);
1437 _views.push_back (DraggingView (_primary, this, v));
1439 _last_frame_position = pos;
1441 _item = _primary->get_canvas_group ();
1445 RegionInsertDrag::finished (GdkEvent *, bool)
1447 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1449 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1450 _primary->get_canvas_group()->set_y_position (0);
1452 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1454 _editor->begin_reversible_command (Operations::insert_region);
1455 playlist->clear_changes ();
1456 playlist->add_region (_primary->region (), _last_frame_position);
1458 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1459 if (Config->get_edit_mode() == Ripple) {
1460 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1463 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1464 _editor->commit_reversible_command ();
1472 RegionInsertDrag::aborted (bool)
1479 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1480 : RegionMoveDrag (e, i, p, v, false, false)
1482 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1485 struct RegionSelectionByPosition {
1486 bool operator() (RegionView*a, RegionView* b) {
1487 return a->region()->position () < b->region()->position();
1492 RegionSpliceDrag::motion (GdkEvent* event, bool)
1494 /* Which trackview is this ? */
1496 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1497 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1499 /* The region motion is only processed if the pointer is over
1503 if (!tv || !tv->is_track()) {
1504 /* To make sure we hide the verbose canvas cursor when the mouse is
1505 not held over an audio track.
1507 _editor->verbose_cursor()->hide ();
1510 _editor->verbose_cursor()->show ();
1515 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1521 RegionSelection copy;
1522 _editor->selection->regions.by_position(copy);
1524 framepos_t const pf = adjusted_current_frame (event);
1526 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1528 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1534 boost::shared_ptr<Playlist> playlist;
1536 if ((playlist = atv->playlist()) == 0) {
1540 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1545 if (pf < (*i)->region()->last_frame() + 1) {
1549 if (pf > (*i)->region()->first_frame()) {
1555 playlist->shuffle ((*i)->region(), dir);
1560 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1562 RegionMoveDrag::finished (event, movement_occurred);
1566 RegionSpliceDrag::aborted (bool)
1576 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1579 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1581 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1582 RegionSelection to_ripple;
1583 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1584 if ((*i)->position() >= where) {
1585 to_ripple.push_back (rtv->view()->find_view(*i));
1589 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1590 if (!exclude.contains (*i)) {
1591 // the selection has already been added to _views
1593 if (drag_in_progress) {
1594 // do the same things that RegionMotionDrag::motion does when
1595 // first_move is true, for the region views that we're adding
1596 // to _views this time
1599 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1600 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1601 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1602 rvg->reparent (_editor->_drag_motion_group);
1604 // we only need to move in the y direction
1605 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1610 _views.push_back (DraggingView (*i, this, tav));
1616 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1619 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1620 // we added all the regions after the selection
1622 std::list<DraggingView>::iterator to_erase = i++;
1623 if (!_editor->selection->regions.contains (to_erase->view)) {
1624 // restore the non-selected regions to their original playlist & positions,
1625 // and then ripple them back by the length of the regions that were dragged away
1626 // do the same things as RegionMotionDrag::aborted
1628 RegionView *rv = to_erase->view;
1629 TimeAxisView* tv = &(rv->get_time_axis_view ());
1630 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1633 // plonk them back onto their own track
1634 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1635 rv->get_canvas_group()->set_y_position (0);
1639 // move the underlying region to match the view
1640 rv->region()->set_position (rv->region()->position() + amount);
1642 // restore the view to match the underlying region's original position
1643 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
1646 rv->set_height (rtv->view()->child_height ());
1647 _views.erase (to_erase);
1653 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const
1655 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) {
1657 return allow_moves_across_tracks;
1665 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1666 : RegionMoveDrag (e, i, p, v, false, false)
1668 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
1669 // compute length of selection
1670 RegionSelection selected_regions = _editor->selection->regions;
1671 selection_length = selected_regions.end_frame() - selected_regions.start();
1673 // we'll only allow dragging to another track in ripple mode if all the regions
1674 // being dragged start off on the same track
1675 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
1678 exclude = new RegionList;
1679 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
1680 exclude->push_back((*i)->region());
1683 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
1684 RegionSelection copy;
1685 selected_regions.by_position(copy); // get selected regions sorted by position into copy
1687 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
1688 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1690 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1691 // find ripple start point on each applicable playlist
1692 RegionView *first_selected_on_this_track = NULL;
1693 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1694 if ((*i)->region()->playlist() == (*pi)) {
1695 // region is on this playlist - it's the first, because they're sorted
1696 first_selected_on_this_track = *i;
1700 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
1701 add_all_after_to_views (
1702 &first_selected_on_this_track->get_time_axis_view(),
1703 first_selected_on_this_track->region()->position(),
1704 selected_regions, false);
1707 if (allow_moves_across_tracks) {
1708 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
1716 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
1718 /* Which trackview is this ? */
1720 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1721 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1723 /* The region motion is only processed if the pointer is over
1727 if (!tv || !tv->is_track()) {
1728 /* To make sure we hide the verbose canvas cursor when the mouse is
1729 not held over an audiotrack.
1731 _editor->verbose_cursor()->hide ();
1735 framepos_t where = adjusted_current_frame (event);
1736 assert (where >= 0);
1738 double delta = compute_x_delta (event, &after);
1740 framecnt_t amount = _editor->pixel_to_sample (delta);
1742 if (allow_moves_across_tracks) {
1743 // all the originally selected regions were on the same track
1745 framecnt_t adjust = 0;
1746 if (prev_tav && tv != prev_tav) {
1747 // dragged onto a different track
1748 // remove the unselected regions from _views, restore them to their original positions
1749 // and add the regions after the drop point on the new playlist to _views instead.
1750 // undo the effect of rippling the previous playlist, and include the effect of removing
1751 // the dragged region(s) from this track
1753 remove_unselected_from_views (prev_amount, false);
1754 // ripple previous playlist according to the regions that have been removed onto the new playlist
1755 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
1758 // move just the selected regions
1759 RegionMoveDrag::motion(event, first_move);
1761 // ensure that the ripple operation on the new playlist inserts selection_length time
1762 adjust = selection_length;
1763 // ripple the new current playlist
1764 tv->playlist()->ripple (where, amount+adjust, exclude);
1766 // add regions after point where drag entered this track to subsequent ripples
1767 add_all_after_to_views (tv, where, _editor->selection->regions, true);
1770 // motion on same track
1771 RegionMoveDrag::motion(event, first_move);
1775 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
1776 prev_position = where;
1778 // selection encompasses multiple tracks - just drag
1779 // cross-track drags are forbidden
1780 RegionMoveDrag::motion(event, first_move);
1783 if (!_x_constrained) {
1784 prev_amount += amount;
1787 _last_frame_position = after;
1791 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
1793 if (!movement_occurred) {
1797 if (was_double_click() && !_views.empty()) {
1798 DraggingView dv = _views.front();
1799 dv.view->show_region_editor ();
1806 _editor->begin_reversible_command(_("Ripple drag"));
1808 // remove the regions being rippled from the dragging view, updating them to
1809 // their new positions
1810 remove_unselected_from_views (prev_amount, true);
1812 if (allow_moves_across_tracks) {
1814 // if regions were dragged across tracks, we've rippled any later
1815 // regions on the track the regions were dragged off, so we need
1816 // to add the original track to the undo record
1817 orig_tav->playlist()->clear_changes();
1818 vector<Command*> cmds;
1819 orig_tav->playlist()->rdiff (cmds);
1820 _editor->session()->add_commands (cmds);
1822 if (prev_tav && prev_tav != orig_tav) {
1823 prev_tav->playlist()->clear_changes();
1824 vector<Command*> cmds;
1825 prev_tav->playlist()->rdiff (cmds);
1826 _editor->session()->add_commands (cmds);
1829 // selection spanned multiple tracks - all will need adding to undo record
1831 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
1832 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1834 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1835 (*pi)->clear_changes();
1836 vector<Command*> cmds;
1837 (*pi)->rdiff (cmds);
1838 _editor->session()->add_commands (cmds);
1842 // other modified playlists are added to undo by RegionMoveDrag::finished()
1843 RegionMoveDrag::finished (event, movement_occurred);
1844 _editor->commit_reversible_command();
1848 RegionRippleDrag::aborted (bool movement_occurred)
1850 RegionMoveDrag::aborted (movement_occurred);
1855 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1857 _view (dynamic_cast<MidiTimeAxisView*> (v))
1859 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1865 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1868 _region = add_midi_region (_view);
1869 _view->playlist()->freeze ();
1872 framepos_t const f = adjusted_current_frame (event);
1873 if (f < grab_frame()) {
1874 _region->set_position (f);
1877 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1878 so that if this region is duplicated, its duplicate starts on
1879 a snap point rather than 1 frame after a snap point. Otherwise things get
1880 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1881 place snapped notes at the start of the region.
1884 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1885 _region->set_length (len < 1 ? 1 : len);
1891 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1893 if (!movement_occurred) {
1894 add_midi_region (_view);
1896 _view->playlist()->thaw ();
1901 RegionCreateDrag::aborted (bool)
1904 _view->playlist()->thaw ();
1910 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1914 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1918 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1920 Gdk::Cursor* cursor;
1921 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1923 float x_fraction = cnote->mouse_x_fraction ();
1925 if (x_fraction > 0.0 && x_fraction < 0.25) {
1926 cursor = _editor->cursors()->left_side_trim;
1929 cursor = _editor->cursors()->right_side_trim;
1933 Drag::start_grab (event, cursor);
1935 region = &cnote->region_view();
1939 if (event->motion.state & Keyboard::PrimaryModifier) {
1945 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1947 if (ms.size() > 1) {
1948 /* has to be relative, may make no sense otherwise */
1952 /* select this note; if it is already selected, preserve the existing selection,
1953 otherwise make this note the only one selected.
1955 region->note_selected (cnote, cnote->selected ());
1957 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1958 MidiRegionSelection::iterator next;
1961 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
1963 mrv->begin_resizing (at_front);
1970 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1972 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1973 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1974 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1976 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
1978 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1984 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1986 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1987 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1988 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1990 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
1992 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1998 NoteResizeDrag::aborted (bool)
2000 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2001 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2002 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2004 mrv->abort_resizing ();
2009 AVDraggingView::AVDraggingView (RegionView* v)
2012 initial_position = v->region()->position ();
2015 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2018 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2021 TrackViewList empty;
2023 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2024 std::list<RegionView*> views = rs.by_layer();
2026 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2027 RegionView* rv = (*i);
2028 if (!rv->region()->video_locked()) {
2031 _views.push_back (AVDraggingView (rv));
2036 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2038 Drag::start_grab (event);
2039 if (_editor->session() == 0) {
2043 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2044 _max_backwards_drag = (
2045 ARDOUR_UI::instance()->video_timeline->get_duration()
2046 + ARDOUR_UI::instance()->video_timeline->get_offset()
2047 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2050 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2051 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2052 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2055 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2058 Timecode::Time timecode;
2059 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2060 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);
2061 show_verbose_cursor_text (buf);
2065 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2067 if (_editor->session() == 0) {
2070 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2074 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2075 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2077 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2078 dt = - _max_backwards_drag;
2081 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2082 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2084 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2085 RegionView* rv = i->view;
2086 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2089 rv->region()->clear_changes ();
2090 rv->region()->suspend_property_changes();
2092 rv->region()->set_position(i->initial_position + dt);
2093 rv->region_changed(ARDOUR::Properties::position);
2096 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2097 Timecode::Time timecode;
2098 Timecode::Time timediff;
2100 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2101 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2102 snprintf (buf, sizeof (buf),
2103 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2104 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2105 , _("Video Start:"),
2106 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2108 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2110 show_verbose_cursor_text (buf);
2114 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2116 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2120 if (!movement_occurred || ! _editor->session()) {
2124 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2126 _editor->begin_reversible_command (_("Move Video"));
2128 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2129 ARDOUR_UI::instance()->video_timeline->save_undo();
2130 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2131 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2133 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2134 i->view->drag_end();
2135 i->view->region()->resume_property_changes ();
2137 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2140 _editor->session()->maybe_update_session_range(
2141 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2142 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2146 _editor->commit_reversible_command ();
2150 VideoTimeLineDrag::aborted (bool)
2152 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2155 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2156 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2158 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2159 i->view->region()->resume_property_changes ();
2160 i->view->region()->set_position(i->initial_position);
2164 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2165 : RegionDrag (e, i, p, v)
2166 , _preserve_fade_anchor (preserve_fade_anchor)
2167 , _jump_position_when_done (false)
2169 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2173 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2176 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2177 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2179 if (tv && tv->is_track()) {
2180 speed = tv->track()->speed();
2183 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2184 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2185 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2187 framepos_t const pf = adjusted_current_frame (event);
2189 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2190 /* Move the contents of the region around without changing the region bounds */
2191 _operation = ContentsTrim;
2192 Drag::start_grab (event, _editor->cursors()->trimmer);
2194 /* These will get overridden for a point trim.*/
2195 if (pf < (region_start + region_length/2)) {
2196 /* closer to front */
2197 _operation = StartTrim;
2199 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2200 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2202 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2206 _operation = EndTrim;
2207 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2208 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2210 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2215 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2216 _jump_position_when_done = true;
2219 switch (_operation) {
2221 show_verbose_cursor_time (region_start);
2222 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2223 i->view->trim_front_starting ();
2227 show_verbose_cursor_time (region_end);
2230 show_verbose_cursor_time (pf);
2234 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2235 i->view->region()->suspend_property_changes ();
2240 TrimDrag::motion (GdkEvent* event, bool first_move)
2242 RegionView* rv = _primary;
2245 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2246 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2247 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2248 frameoffset_t frame_delta = 0;
2250 if (tv && tv->is_track()) {
2251 speed = tv->track()->speed();
2254 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2260 switch (_operation) {
2262 trim_type = "Region start trim";
2265 trim_type = "Region end trim";
2268 trim_type = "Region content trim";
2275 _editor->begin_reversible_command (trim_type);
2277 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2278 RegionView* rv = i->view;
2279 rv->enable_display (false);
2280 rv->region()->playlist()->clear_owned_changes ();
2282 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2285 arv->temporarily_hide_envelope ();
2289 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2290 insert_result = _editor->motion_frozen_playlists.insert (pl);
2292 if (insert_result.second) {
2298 bool non_overlap_trim = false;
2300 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2301 non_overlap_trim = true;
2304 /* contstrain trim to fade length */
2305 if (_preserve_fade_anchor) {
2306 switch (_operation) {
2308 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2309 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2311 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2312 if (ar->locked()) continue;
2313 framecnt_t len = ar->fade_in()->back()->when;
2314 if (len < dt) dt = min(dt, len);
2318 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2319 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2321 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2322 if (ar->locked()) continue;
2323 framecnt_t len = ar->fade_out()->back()->when;
2324 if (len < -dt) dt = max(dt, -len);
2333 switch (_operation) {
2335 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2336 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2337 if (changed && _preserve_fade_anchor) {
2338 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2340 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2341 framecnt_t len = ar->fade_in()->back()->when;
2342 framecnt_t diff = ar->first_frame() - i->initial_position;
2343 framepos_t new_length = len - diff;
2344 i->anchored_fade_length = min (ar->length(), new_length);
2345 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2346 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2353 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2354 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2355 if (changed && _preserve_fade_anchor) {
2356 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2358 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2359 framecnt_t len = ar->fade_out()->back()->when;
2360 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2361 framepos_t new_length = len + diff;
2362 i->anchored_fade_length = min (ar->length(), new_length);
2363 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2364 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2372 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2374 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2375 i->view->move_contents (frame_delta);
2381 switch (_operation) {
2383 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2386 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2389 // show_verbose_cursor_time (frame_delta);
2396 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2398 if (movement_occurred) {
2399 motion (event, false);
2401 if (_operation == StartTrim) {
2402 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2404 /* This must happen before the region's StatefulDiffCommand is created, as it may
2405 `correct' (ahem) the region's _start from being negative to being zero. It
2406 needs to be zero in the undo record.
2408 i->view->trim_front_ending ();
2410 if (_preserve_fade_anchor) {
2411 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2413 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2414 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2415 ar->set_fade_in_length(i->anchored_fade_length);
2416 ar->set_fade_in_active(true);
2419 if (_jump_position_when_done) {
2420 i->view->region()->set_position (i->initial_position);
2423 } else if (_operation == EndTrim) {
2424 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2425 if (_preserve_fade_anchor) {
2426 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2428 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2429 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2430 ar->set_fade_out_length(i->anchored_fade_length);
2431 ar->set_fade_out_active(true);
2434 if (_jump_position_when_done) {
2435 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2440 if (!_views.empty()) {
2441 if (_operation == StartTrim) {
2442 _editor->maybe_locate_with_edit_preroll(
2443 _views.begin()->view->region()->position());
2445 if (_operation == EndTrim) {
2446 _editor->maybe_locate_with_edit_preroll(
2447 _views.begin()->view->region()->position() +
2448 _views.begin()->view->region()->length());
2452 if (!_editor->selection->selected (_primary)) {
2453 _primary->thaw_after_trim ();
2456 set<boost::shared_ptr<Playlist> > diffed_playlists;
2458 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2459 i->view->thaw_after_trim ();
2460 i->view->enable_display (true);
2462 /* Trimming one region may affect others on the playlist, so we need
2463 to get undo Commands from the whole playlist rather than just the
2464 region. Use diffed_playlists to make sure we don't diff a given
2465 playlist more than once.
2467 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2468 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2469 vector<Command*> cmds;
2471 _editor->session()->add_commands (cmds);
2472 diffed_playlists.insert (p);
2477 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2481 _editor->motion_frozen_playlists.clear ();
2482 _editor->commit_reversible_command();
2485 /* no mouse movement */
2486 _editor->point_trim (event, adjusted_current_frame (event));
2489 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2490 if (_operation == StartTrim) {
2491 i->view->trim_front_ending ();
2494 i->view->region()->resume_property_changes ();
2499 TrimDrag::aborted (bool movement_occurred)
2501 /* Our motion method is changing model state, so use the Undo system
2502 to cancel. Perhaps not ideal, as this will leave an Undo point
2503 behind which may be slightly odd from the user's point of view.
2508 if (movement_occurred) {
2512 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2513 i->view->region()->resume_property_changes ();
2518 TrimDrag::setup_pointer_frame_offset ()
2520 list<DraggingView>::iterator i = _views.begin ();
2521 while (i != _views.end() && i->view != _primary) {
2525 if (i == _views.end()) {
2529 switch (_operation) {
2531 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2534 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2541 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2545 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2546 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2551 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2553 Drag::start_grab (event, cursor);
2554 show_verbose_cursor_time (adjusted_current_frame(event));
2558 MeterMarkerDrag::setup_pointer_frame_offset ()
2560 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2564 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2566 if (!_marker->meter().movable()) {
2572 // create a dummy marker for visual representation of moving the
2573 // section, because whether its a copy or not, we're going to
2574 // leave or lose the original marker (leave if its a copy; lose if its
2575 // not, because we'll remove it from the map).
2577 MeterSection section (_marker->meter());
2579 if (!section.movable()) {
2584 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2586 _marker = new MeterMarker (
2588 *_editor->meter_group,
2589 ARDOUR_UI::config()->get_MeterMarker(),
2591 *new MeterSection (_marker->meter())
2594 /* use the new marker for the grab */
2595 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2598 TempoMap& map (_editor->session()->tempo_map());
2599 /* get current state */
2600 before_state = &map.get_state();
2601 /* remove the section while we drag it */
2602 map.remove_meter (section, true);
2606 framepos_t const pf = adjusted_current_frame (event);
2607 _marker->set_position (pf);
2608 show_verbose_cursor_time (pf);
2612 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2614 if (!movement_occurred) {
2615 if (was_double_click()) {
2616 _editor->edit_meter_marker (*_marker);
2621 if (!_marker->meter().movable()) {
2625 motion (event, false);
2627 Timecode::BBT_Time when;
2629 TempoMap& map (_editor->session()->tempo_map());
2630 map.bbt_time (last_pointer_frame(), when);
2632 if (_copy == true) {
2633 _editor->begin_reversible_command (_("copy meter mark"));
2634 XMLNode &before = map.get_state();
2635 map.add_meter (_marker->meter(), when);
2636 XMLNode &after = map.get_state();
2637 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2638 _editor->commit_reversible_command ();
2641 _editor->begin_reversible_command (_("move meter mark"));
2643 /* we removed it before, so add it back now */
2645 map.add_meter (_marker->meter(), when);
2646 XMLNode &after = map.get_state();
2647 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2648 _editor->commit_reversible_command ();
2651 // delete the dummy marker we used for visual representation while moving.
2652 // a new visual marker will show up automatically.
2657 MeterMarkerDrag::aborted (bool moved)
2659 _marker->set_position (_marker->meter().frame ());
2662 TempoMap& map (_editor->session()->tempo_map());
2663 /* we removed it before, so add it back now */
2664 map.add_meter (_marker->meter(), _marker->meter().frame());
2665 // delete the dummy marker we used for visual representation while moving.
2666 // a new visual marker will show up automatically.
2671 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2675 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2677 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2682 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2684 Drag::start_grab (event, cursor);
2685 show_verbose_cursor_time (adjusted_current_frame (event));
2689 TempoMarkerDrag::setup_pointer_frame_offset ()
2691 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2695 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2697 if (!_marker->tempo().movable()) {
2703 // create a dummy marker for visual representation of moving the
2704 // section, because whether its a copy or not, we're going to
2705 // leave or lose the original marker (leave if its a copy; lose if its
2706 // not, because we'll remove it from the map).
2708 // create a dummy marker for visual representation of moving the copy.
2709 // The actual copying is not done before we reach the finish callback.
2712 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2714 TempoSection section (_marker->tempo());
2716 _marker = new TempoMarker (
2718 *_editor->tempo_group,
2719 ARDOUR_UI::config()->get_TempoMarker(),
2721 *new TempoSection (_marker->tempo())
2724 /* use the new marker for the grab */
2725 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2728 TempoMap& map (_editor->session()->tempo_map());
2729 /* get current state */
2730 before_state = &map.get_state();
2731 /* remove the section while we drag it */
2732 map.remove_tempo (section, true);
2736 framepos_t const pf = adjusted_current_frame (event);
2737 _marker->set_position (pf);
2738 show_verbose_cursor_time (pf);
2742 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2744 if (!movement_occurred) {
2745 if (was_double_click()) {
2746 _editor->edit_tempo_marker (*_marker);
2751 if (!_marker->tempo().movable()) {
2755 motion (event, false);
2757 TempoMap& map (_editor->session()->tempo_map());
2758 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
2759 Timecode::BBT_Time when;
2761 map.bbt_time (beat_time, when);
2763 if (_copy == true) {
2764 _editor->begin_reversible_command (_("copy tempo mark"));
2765 XMLNode &before = map.get_state();
2766 map.add_tempo (_marker->tempo(), when);
2767 XMLNode &after = map.get_state();
2768 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2769 _editor->commit_reversible_command ();
2772 _editor->begin_reversible_command (_("move tempo mark"));
2773 /* we removed it before, so add it back now */
2774 map.add_tempo (_marker->tempo(), when);
2775 XMLNode &after = map.get_state();
2776 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2777 _editor->commit_reversible_command ();
2780 // delete the dummy marker we used for visual representation while moving.
2781 // a new visual marker will show up automatically.
2786 TempoMarkerDrag::aborted (bool moved)
2788 _marker->set_position (_marker->tempo().frame());
2790 TempoMap& map (_editor->session()->tempo_map());
2791 /* we removed it before, so add it back now */
2792 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2793 // delete the dummy marker we used for visual representation while moving.
2794 // a new visual marker will show up automatically.
2799 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2800 : Drag (e, &c.track_canvas_item(), false)
2804 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2807 /** Do all the things we do when dragging the playhead to make it look as though
2808 * we have located, without actually doing the locate (because that would cause
2809 * the diskstream buffers to be refilled, which is too slow).
2812 CursorDrag::fake_locate (framepos_t t)
2814 _editor->playhead_cursor->set_position (t);
2816 Session* s = _editor->session ();
2817 if (s->timecode_transmission_suspended ()) {
2818 framepos_t const f = _editor->playhead_cursor->current_frame ();
2819 /* This is asynchronous so it will be sent "now"
2821 s->send_mmc_locate (f);
2822 /* These are synchronous and will be sent during the next
2825 s->queue_full_time_code ();
2826 s->queue_song_position_pointer ();
2829 show_verbose_cursor_time (t);
2830 _editor->UpdateAllTransportClocks (t);
2834 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2836 Drag::start_grab (event, c);
2838 _grab_zoom = _editor->samples_per_pixel;
2840 framepos_t where = _editor->canvas_event_sample (event);
2842 _editor->snap_to_with_modifier (where, event);
2844 _editor->_dragging_playhead = true;
2846 Session* s = _editor->session ();
2848 /* grab the track canvas item as well */
2850 _cursor.track_canvas_item().grab();
2853 if (_was_rolling && _stop) {
2857 if (s->is_auditioning()) {
2858 s->cancel_audition ();
2862 if (AudioEngine::instance()->connected()) {
2864 /* do this only if we're the engine is connected
2865 * because otherwise this request will never be
2866 * serviced and we'll busy wait forever. likewise,
2867 * notice if we are disconnected while waiting for the
2868 * request to be serviced.
2871 s->request_suspend_timecode_transmission ();
2872 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2873 /* twiddle our thumbs */
2878 fake_locate (where);
2882 CursorDrag::motion (GdkEvent* event, bool)
2884 framepos_t const adjusted_frame = adjusted_current_frame (event);
2885 if (adjusted_frame != last_pointer_frame()) {
2886 fake_locate (adjusted_frame);
2891 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2893 _editor->_dragging_playhead = false;
2895 _cursor.track_canvas_item().ungrab();
2897 if (!movement_occurred && _stop) {
2901 motion (event, false);
2903 Session* s = _editor->session ();
2905 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2906 _editor->_pending_locate_request = true;
2907 s->request_resume_timecode_transmission ();
2912 CursorDrag::aborted (bool)
2914 _cursor.track_canvas_item().ungrab();
2916 if (_editor->_dragging_playhead) {
2917 _editor->session()->request_resume_timecode_transmission ();
2918 _editor->_dragging_playhead = false;
2921 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2924 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2925 : RegionDrag (e, i, p, v)
2927 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2931 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2933 Drag::start_grab (event, cursor);
2935 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2936 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2938 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2942 FadeInDrag::setup_pointer_frame_offset ()
2944 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2945 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2946 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2950 FadeInDrag::motion (GdkEvent* event, bool)
2952 framecnt_t fade_length;
2954 framepos_t const pos = adjusted_current_frame (event);
2956 boost::shared_ptr<Region> region = _primary->region ();
2958 if (pos < (region->position() + 64)) {
2959 fade_length = 64; // this should be a minimum defined somewhere
2960 } else if (pos > region->last_frame()) {
2961 fade_length = region->length();
2963 fade_length = pos - region->position();
2966 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2968 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2974 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2977 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2981 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2983 if (!movement_occurred) {
2987 framecnt_t fade_length;
2989 framepos_t const pos = adjusted_current_frame (event);
2991 boost::shared_ptr<Region> region = _primary->region ();
2993 if (pos < (region->position() + 64)) {
2994 fade_length = 64; // this should be a minimum defined somewhere
2995 } else if (pos > region->last_frame()) {
2996 fade_length = region->length();
2998 fade_length = pos - region->position();
3001 _editor->begin_reversible_command (_("change fade in length"));
3003 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3005 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3011 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3012 XMLNode &before = alist->get_state();
3014 tmp->audio_region()->set_fade_in_length (fade_length);
3015 tmp->audio_region()->set_fade_in_active (true);
3017 XMLNode &after = alist->get_state();
3018 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3021 _editor->commit_reversible_command ();
3025 FadeInDrag::aborted (bool)
3027 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3028 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3034 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3038 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3039 : RegionDrag (e, i, p, v)
3041 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3045 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3047 Drag::start_grab (event, cursor);
3049 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3050 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3052 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3056 FadeOutDrag::setup_pointer_frame_offset ()
3058 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3059 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3060 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3064 FadeOutDrag::motion (GdkEvent* event, bool)
3066 framecnt_t fade_length;
3068 framepos_t const pos = adjusted_current_frame (event);
3070 boost::shared_ptr<Region> region = _primary->region ();
3072 if (pos > (region->last_frame() - 64)) {
3073 fade_length = 64; // this should really be a minimum fade defined somewhere
3075 else if (pos < region->position()) {
3076 fade_length = region->length();
3079 fade_length = region->last_frame() - pos;
3082 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3084 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3090 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3093 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3097 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3099 if (!movement_occurred) {
3103 framecnt_t fade_length;
3105 framepos_t const pos = adjusted_current_frame (event);
3107 boost::shared_ptr<Region> region = _primary->region ();
3109 if (pos > (region->last_frame() - 64)) {
3110 fade_length = 64; // this should really be a minimum fade defined somewhere
3112 else if (pos < region->position()) {
3113 fade_length = region->length();
3116 fade_length = region->last_frame() - pos;
3119 _editor->begin_reversible_command (_("change fade out length"));
3121 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3123 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3129 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3130 XMLNode &before = alist->get_state();
3132 tmp->audio_region()->set_fade_out_length (fade_length);
3133 tmp->audio_region()->set_fade_out_active (true);
3135 XMLNode &after = alist->get_state();
3136 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3139 _editor->commit_reversible_command ();
3143 FadeOutDrag::aborted (bool)
3145 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3146 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3152 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3156 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3159 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3161 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3164 _points.push_back (ArdourCanvas::Duple (0, 0));
3165 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3168 MarkerDrag::~MarkerDrag ()
3170 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3175 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3177 location = new Location (*l);
3178 markers.push_back (m);
3183 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3185 Drag::start_grab (event, cursor);
3189 Location *location = _editor->find_location_from_marker (_marker, is_start);
3190 _editor->_dragging_edit_point = true;
3192 update_item (location);
3194 // _drag_line->show();
3195 // _line->raise_to_top();
3198 show_verbose_cursor_time (location->start());
3200 show_verbose_cursor_time (location->end());
3203 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3206 case Selection::Toggle:
3207 /* we toggle on the button release */
3209 case Selection::Set:
3210 if (!_editor->selection->selected (_marker)) {
3211 _editor->selection->set (_marker);
3214 case Selection::Extend:
3216 Locations::LocationList ll;
3217 list<Marker*> to_add;
3219 _editor->selection->markers.range (s, e);
3220 s = min (_marker->position(), s);
3221 e = max (_marker->position(), e);
3224 if (e < max_framepos) {
3227 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3228 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3229 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3232 to_add.push_back (lm->start);
3235 to_add.push_back (lm->end);
3239 if (!to_add.empty()) {
3240 _editor->selection->add (to_add);
3244 case Selection::Add:
3245 _editor->selection->add (_marker);
3249 /* Set up copies for us to manipulate during the drag
3252 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3254 Location* l = _editor->find_location_from_marker (*i, is_start);
3261 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3263 /* range: check that the other end of the range isn't
3266 CopiedLocationInfo::iterator x;
3267 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3268 if (*(*x).location == *l) {
3272 if (x == _copied_locations.end()) {
3273 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3275 (*x).markers.push_back (*i);
3276 (*x).move_both = true;
3284 MarkerDrag::setup_pointer_frame_offset ()
3287 Location *location = _editor->find_location_from_marker (_marker, is_start);
3288 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3292 MarkerDrag::motion (GdkEvent* event, bool)
3294 framecnt_t f_delta = 0;
3296 bool move_both = false;
3297 Location *real_location;
3298 Location *copy_location = 0;
3300 framepos_t const newframe = adjusted_current_frame (event);
3301 framepos_t next = newframe;
3303 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3307 CopiedLocationInfo::iterator x;
3309 /* find the marker we're dragging, and compute the delta */
3311 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3313 copy_location = (*x).location;
3315 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3317 /* this marker is represented by this
3318 * CopiedLocationMarkerInfo
3321 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3326 if (real_location->is_mark()) {
3327 f_delta = newframe - copy_location->start();
3331 switch (_marker->type()) {
3332 case Marker::SessionStart:
3333 case Marker::RangeStart:
3334 case Marker::LoopStart:
3335 case Marker::PunchIn:
3336 f_delta = newframe - copy_location->start();
3339 case Marker::SessionEnd:
3340 case Marker::RangeEnd:
3341 case Marker::LoopEnd:
3342 case Marker::PunchOut:
3343 f_delta = newframe - copy_location->end();
3346 /* what kind of marker is this ? */
3355 if (x == _copied_locations.end()) {
3356 /* hmm, impossible - we didn't find the dragged marker */
3360 /* now move them all */
3362 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3364 copy_location = x->location;
3366 /* call this to find out if its the start or end */
3368 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3372 if (real_location->locked()) {
3376 if (copy_location->is_mark()) {
3380 copy_location->set_start (copy_location->start() + f_delta);
3384 framepos_t new_start = copy_location->start() + f_delta;
3385 framepos_t new_end = copy_location->end() + f_delta;
3387 if (is_start) { // start-of-range marker
3389 if (move_both || (*x).move_both) {
3390 copy_location->set_start (new_start);
3391 copy_location->set_end (new_end);
3392 } else if (new_start < copy_location->end()) {
3393 copy_location->set_start (new_start);
3394 } else if (newframe > 0) {
3395 _editor->snap_to (next, RoundUpAlways, true);
3396 copy_location->set_end (next);
3397 copy_location->set_start (newframe);
3400 } else { // end marker
3402 if (move_both || (*x).move_both) {
3403 copy_location->set_end (new_end);
3404 copy_location->set_start (new_start);
3405 } else if (new_end > copy_location->start()) {
3406 copy_location->set_end (new_end);
3407 } else if (newframe > 0) {
3408 _editor->snap_to (next, RoundDownAlways, true);
3409 copy_location->set_start (next);
3410 copy_location->set_end (newframe);
3415 update_item (copy_location);
3417 /* now lookup the actual GUI items used to display this
3418 * location and move them to wherever the copy of the location
3419 * is now. This means that the logic in ARDOUR::Location is
3420 * still enforced, even though we are not (yet) modifying
3421 * the real Location itself.
3424 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3427 lm->set_position (copy_location->start(), copy_location->end());
3432 assert (!_copied_locations.empty());
3434 show_verbose_cursor_time (newframe);
3438 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3440 if (!movement_occurred) {
3442 if (was_double_click()) {
3443 _editor->rename_marker (_marker);
3447 /* just a click, do nothing but finish
3448 off the selection process
3451 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3454 case Selection::Set:
3455 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3456 _editor->selection->set (_marker);
3460 case Selection::Toggle:
3461 /* we toggle on the button release, click only */
3462 _editor->selection->toggle (_marker);
3465 case Selection::Extend:
3466 case Selection::Add:
3473 _editor->_dragging_edit_point = false;
3475 _editor->begin_reversible_command ( _("move marker") );
3476 XMLNode &before = _editor->session()->locations()->get_state();
3478 MarkerSelection::iterator i;
3479 CopiedLocationInfo::iterator x;
3482 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3483 x != _copied_locations.end() && i != _editor->selection->markers.end();
3486 Location * location = _editor->find_location_from_marker (*i, is_start);
3490 if (location->locked()) {
3494 if (location->is_mark()) {
3495 location->set_start (((*x).location)->start());
3497 location->set (((*x).location)->start(), ((*x).location)->end());
3502 XMLNode &after = _editor->session()->locations()->get_state();
3503 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3504 _editor->commit_reversible_command ();
3508 MarkerDrag::aborted (bool)
3514 MarkerDrag::update_item (Location*)
3519 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3521 _cumulative_x_drag (0),
3522 _cumulative_y_drag (0)
3524 if (_zero_gain_fraction < 0.0) {
3525 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3528 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3530 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3536 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3538 Drag::start_grab (event, _editor->cursors()->fader);
3540 // start the grab at the center of the control point so
3541 // the point doesn't 'jump' to the mouse after the first drag
3542 _fixed_grab_x = _point->get_x();
3543 _fixed_grab_y = _point->get_y();
3545 float const fraction = 1 - (_point->get_y() / _point->line().height());
3547 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3549 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3551 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3553 if (!_point->can_slide ()) {
3554 _x_constrained = true;
3559 ControlPointDrag::motion (GdkEvent* event, bool)
3561 double dx = _drags->current_pointer_x() - last_pointer_x();
3562 double dy = current_pointer_y() - last_pointer_y();
3564 if (event->button.state & Keyboard::SecondaryModifier) {
3569 /* coordinate in pixels relative to the start of the region (for region-based automation)
3570 or track (for track-based automation) */
3571 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3572 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3574 // calculate zero crossing point. back off by .01 to stay on the
3575 // positive side of zero
3576 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3578 // make sure we hit zero when passing through
3579 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3583 if (_x_constrained) {
3586 if (_y_constrained) {
3590 _cumulative_x_drag = cx - _fixed_grab_x;
3591 _cumulative_y_drag = cy - _fixed_grab_y;
3595 cy = min ((double) _point->line().height(), cy);
3597 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3599 if (!_x_constrained) {
3600 _editor->snap_to_with_modifier (cx_frames, event);
3603 cx_frames = min (cx_frames, _point->line().maximum_time());
3605 float const fraction = 1.0 - (cy / _point->line().height());
3607 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3609 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3613 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3615 if (!movement_occurred) {
3619 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3620 _editor->reset_point_selection ();
3624 motion (event, false);
3627 _point->line().end_drag (_pushing, _final_index);
3628 _editor->session()->commit_reversible_command ();
3632 ControlPointDrag::aborted (bool)
3634 _point->line().reset ();
3638 ControlPointDrag::active (Editing::MouseMode m)
3640 if (m == Editing::MouseGain) {
3641 /* always active in mouse gain */
3645 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3646 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3649 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3652 _cumulative_y_drag (0)
3654 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3658 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3660 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3663 _item = &_line->grab_item ();
3665 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3666 origin, and ditto for y.
3669 double cx = event->button.x;
3670 double cy = event->button.y;
3672 _line->parent_group().canvas_to_item (cx, cy);
3674 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3679 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3680 /* no adjacent points */
3684 Drag::start_grab (event, _editor->cursors()->fader);
3686 /* store grab start in parent frame */
3691 double fraction = 1.0 - (cy / _line->height());
3693 _line->start_drag_line (before, after, fraction);
3695 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3699 LineDrag::motion (GdkEvent* event, bool)
3701 double dy = current_pointer_y() - last_pointer_y();
3703 if (event->button.state & Keyboard::SecondaryModifier) {
3707 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3709 _cumulative_y_drag = cy - _fixed_grab_y;
3712 cy = min ((double) _line->height(), cy);
3714 double const fraction = 1.0 - (cy / _line->height());
3717 /* we are ignoring x position for this drag, so we can just pass in anything */
3718 _line->drag_motion (0, fraction, true, false, ignored);
3720 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3724 LineDrag::finished (GdkEvent* event, bool movement_occured)
3726 if (movement_occured) {
3727 motion (event, false);
3728 _line->end_drag (false, 0);
3730 /* add a new control point on the line */
3732 AutomationTimeAxisView* atv;
3734 _line->end_drag (false, 0);
3736 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3737 framepos_t where = _editor->window_event_sample (event, 0, 0);
3738 atv->add_automation_event (event, where, event->button.y, false);
3742 _editor->session()->commit_reversible_command ();
3746 LineDrag::aborted (bool)
3751 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3754 _cumulative_x_drag (0)
3756 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3760 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3762 Drag::start_grab (event);
3764 _line = reinterpret_cast<Line*> (_item);
3767 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3769 double cx = event->button.x;
3770 double cy = event->button.y;
3772 _item->parent()->canvas_to_item (cx, cy);
3774 /* store grab start in parent frame */
3775 _region_view_grab_x = cx;
3777 _before = *(float*) _item->get_data ("position");
3779 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3781 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3785 FeatureLineDrag::motion (GdkEvent*, bool)
3787 double dx = _drags->current_pointer_x() - last_pointer_x();
3789 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3791 _cumulative_x_drag += dx;
3793 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3802 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3804 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3806 float *pos = new float;
3809 _line->set_data ("position", pos);
3815 FeatureLineDrag::finished (GdkEvent*, bool)
3817 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3818 _arv->update_transient(_before, _before);
3822 FeatureLineDrag::aborted (bool)
3827 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3829 , _vertical_only (false)
3831 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3835 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3837 Drag::start_grab (event);
3838 show_verbose_cursor_time (adjusted_current_frame (event));
3842 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3849 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3851 framepos_t grab = grab_frame ();
3852 if (Config->get_rubberbanding_snaps_to_grid ()) {
3853 _editor->snap_to_with_modifier (grab, event);
3856 /* base start and end on initial click position */
3866 if (current_pointer_y() < grab_y()) {
3867 y1 = current_pointer_y();
3870 y2 = current_pointer_y();
3874 if (start != end || y1 != y2) {
3876 double x1 = _editor->sample_to_pixel (start);
3877 double x2 = _editor->sample_to_pixel (end);
3878 const double min_dimension = 2.0;
3880 if (_vertical_only) {
3881 /* fixed 10 pixel width */
3885 x2 = min (x1 - min_dimension, x2);
3887 x2 = max (x1 + min_dimension, x2);
3892 y2 = min (y1 - min_dimension, y2);
3894 y2 = max (y1 + min_dimension, y2);
3897 /* translate rect into item space and set */
3899 ArdourCanvas::Rect r (x1, y1, x2, y2);
3901 /* this drag is a _trackview_only == true drag, so the y1 and
3902 * y2 (computed using current_pointer_y() and grab_y()) will be
3903 * relative to the top of the trackview group). The
3904 * rubberband rect has the same parent/scroll offset as the
3905 * the trackview group, so we can use the "r" rect directly
3906 * to set the shape of the rubberband.
3909 _editor->rubberband_rect->set (r);
3910 _editor->rubberband_rect->show();
3911 _editor->rubberband_rect->raise_to_top();
3913 show_verbose_cursor_time (pf);
3915 do_select_things (event, true);
3920 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3925 if (grab_frame() < last_pointer_frame()) {
3927 x2 = last_pointer_frame ();
3930 x1 = last_pointer_frame ();
3936 if (current_pointer_y() < grab_y()) {
3937 y1 = current_pointer_y();
3940 y2 = current_pointer_y();
3944 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3948 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3950 if (movement_occurred) {
3952 motion (event, false);
3953 do_select_things (event, false);
3959 bool do_deselect = true;
3960 MidiTimeAxisView* mtv;
3962 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3964 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
3965 /* nothing selected */
3966 add_midi_region (mtv);
3967 do_deselect = false;
3971 /* do not deselect if Primary or Tertiary (toggle-select or
3972 * extend-select are pressed.
3975 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3976 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3983 _editor->rubberband_rect->hide();
3987 RubberbandSelectDrag::aborted (bool)
3989 _editor->rubberband_rect->hide ();
3992 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3993 : RegionDrag (e, i, p, v)
3995 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3999 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4001 Drag::start_grab (event, cursor);
4003 show_verbose_cursor_time (adjusted_current_frame (event));
4007 TimeFXDrag::motion (GdkEvent* event, bool)
4009 RegionView* rv = _primary;
4010 StreamView* cv = rv->get_time_axis_view().view ();
4012 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4013 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4014 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4016 framepos_t const pf = adjusted_current_frame (event);
4018 if (pf > rv->region()->position()) {
4019 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4022 show_verbose_cursor_time (pf);
4026 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4028 _primary->get_time_axis_view().hide_timestretch ();
4030 if (!movement_occurred) {
4034 if (last_pointer_frame() < _primary->region()->position()) {
4035 /* backwards drag of the left edge - not usable */
4039 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4041 float percentage = (double) newlen / (double) _primary->region()->length();
4043 #ifndef USE_RUBBERBAND
4044 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4045 if (_primary->region()->data_type() == DataType::AUDIO) {
4046 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4050 if (!_editor->get_selection().regions.empty()) {
4051 /* primary will already be included in the selection, and edit
4052 group shared editing will propagate selection across
4053 equivalent regions, so just use the current region
4057 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4058 error << _("An error occurred while executing time stretch operation") << endmsg;
4064 TimeFXDrag::aborted (bool)
4066 _primary->get_time_axis_view().hide_timestretch ();
4069 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4072 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4076 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4078 Drag::start_grab (event);
4082 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4084 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4088 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4090 if (movement_occurred && _editor->session()) {
4091 /* make sure we stop */
4092 _editor->session()->request_transport_speed (0.0);
4097 ScrubDrag::aborted (bool)
4102 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4107 , _original_pointer_time_axis (-1)
4108 , _last_pointer_time_axis (-1)
4109 , _time_selection_at_start (!_editor->get_selection().time.empty())
4111 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4113 if (_time_selection_at_start) {
4114 start_at_start = _editor->get_selection().time.start();
4115 end_at_start = _editor->get_selection().time.end_frame();
4120 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4122 if (_editor->session() == 0) {
4126 Gdk::Cursor* cursor = 0;
4128 switch (_operation) {
4129 case CreateSelection:
4130 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4135 cursor = _editor->cursors()->selector;
4136 Drag::start_grab (event, cursor);
4139 case SelectionStartTrim:
4140 if (_editor->clicked_axisview) {
4141 _editor->clicked_axisview->order_selection_trims (_item, true);
4143 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4146 case SelectionEndTrim:
4147 if (_editor->clicked_axisview) {
4148 _editor->clicked_axisview->order_selection_trims (_item, false);
4150 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4154 Drag::start_grab (event, cursor);
4157 case SelectionExtend:
4158 Drag::start_grab (event, cursor);
4162 if (_operation == SelectionMove) {
4163 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4165 show_verbose_cursor_time (adjusted_current_frame (event));
4168 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4172 SelectionDrag::setup_pointer_frame_offset ()
4174 switch (_operation) {
4175 case CreateSelection:
4176 _pointer_frame_offset = 0;
4179 case SelectionStartTrim:
4181 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4184 case SelectionEndTrim:
4185 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4188 case SelectionExtend:
4194 SelectionDrag::motion (GdkEvent* event, bool first_move)
4196 framepos_t start = 0;
4198 framecnt_t length = 0;
4199 framecnt_t distance = 0;
4201 framepos_t const pending_position = adjusted_current_frame (event);
4203 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4207 switch (_operation) {
4208 case CreateSelection:
4210 framepos_t grab = grab_frame ();
4213 grab = adjusted_current_frame (event, false);
4214 if (grab < pending_position) {
4215 _editor->snap_to (grab, RoundDownMaybe);
4217 _editor->snap_to (grab, RoundUpMaybe);
4221 if (pending_position < grab) {
4222 start = pending_position;
4225 end = pending_position;
4229 /* first drag: Either add to the selection
4230 or create a new selection
4237 /* adding to the selection */
4238 _editor->set_selected_track_as_side_effect (Selection::Add);
4239 _editor->clicked_selection = _editor->selection->add (start, end);
4246 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4247 _editor->set_selected_track_as_side_effect (Selection::Set);
4250 _editor->clicked_selection = _editor->selection->set (start, end);
4254 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4255 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4256 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4258 _editor->selection->add (atest);
4262 /* select all tracks within the rectangle that we've marked out so far */
4263 TrackViewList new_selection;
4264 TrackViewList& all_tracks (_editor->track_views);
4266 ArdourCanvas::Coord const top = grab_y();
4267 ArdourCanvas::Coord const bottom = current_pointer_y();
4269 if (top >= 0 && bottom >= 0) {
4271 //first, find the tracks that are covered in the y range selection
4272 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4273 if ((*i)->covered_by_y_range (top, bottom)) {
4274 new_selection.push_back (*i);
4278 //now find any tracks that are GROUPED with the tracks we selected
4279 TrackViewList grouped_add = new_selection;
4280 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4281 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4282 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4283 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4284 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4285 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4286 grouped_add.push_back (*j);
4291 //now compare our list with the current selection, and add or remove as necessary
4292 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4293 TrackViewList tracks_to_add;
4294 TrackViewList tracks_to_remove;
4295 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4296 if ( !_editor->selection->tracks.contains ( *i ) )
4297 tracks_to_add.push_back ( *i );
4298 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4299 if ( !grouped_add.contains ( *i ) )
4300 tracks_to_remove.push_back ( *i );
4301 _editor->selection->add(tracks_to_add);
4302 _editor->selection->remove(tracks_to_remove);
4308 case SelectionStartTrim:
4310 start = _editor->selection->time[_editor->clicked_selection].start;
4311 end = _editor->selection->time[_editor->clicked_selection].end;
4313 if (pending_position > end) {
4316 start = pending_position;
4320 case SelectionEndTrim:
4322 start = _editor->selection->time[_editor->clicked_selection].start;
4323 end = _editor->selection->time[_editor->clicked_selection].end;
4325 if (pending_position < start) {
4328 end = pending_position;
4335 start = _editor->selection->time[_editor->clicked_selection].start;
4336 end = _editor->selection->time[_editor->clicked_selection].end;
4338 length = end - start;
4339 distance = pending_position - start;
4340 start = pending_position;
4341 _editor->snap_to (start);
4343 end = start + length;
4347 case SelectionExtend:
4352 switch (_operation) {
4354 if (_time_selection_at_start) {
4355 _editor->selection->move_time (distance);
4359 _editor->selection->replace (_editor->clicked_selection, start, end);
4363 if (_operation == SelectionMove) {
4364 show_verbose_cursor_time(start);
4366 show_verbose_cursor_time(pending_position);
4371 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4373 Session* s = _editor->session();
4375 if (movement_occurred) {
4376 motion (event, false);
4377 /* XXX this is not object-oriented programming at all. ick */
4378 if (_editor->selection->time.consolidate()) {
4379 _editor->selection->TimeChanged ();
4382 /* XXX what if its a music time selection? */
4384 if ( s->get_play_range() && s->transport_rolling() ) {
4385 s->request_play_range (&_editor->selection->time, true);
4387 if (Config->get_follow_edits() && !s->transport_rolling()) {
4388 if (_operation == SelectionEndTrim)
4389 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4391 s->request_locate (_editor->get_selection().time.start());
4397 /* just a click, no pointer movement.
4400 if (_operation == SelectionExtend) {
4401 if (_time_selection_at_start) {
4402 framepos_t pos = adjusted_current_frame (event, false);
4403 framepos_t start = min (pos, start_at_start);
4404 framepos_t end = max (pos, end_at_start);
4405 _editor->selection->set (start, end);
4408 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4409 if (_editor->clicked_selection) {
4410 _editor->selection->remove (_editor->clicked_selection);
4413 if (!_editor->clicked_selection) {
4414 _editor->selection->clear_time();
4419 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4420 _editor->selection->set (_editor->clicked_axisview);
4423 if (s && s->get_play_range () && s->transport_rolling()) {
4424 s->request_stop (false, false);
4429 _editor->stop_canvas_autoscroll ();
4430 _editor->clicked_selection = 0;
4434 SelectionDrag::aborted (bool)
4439 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4440 : Drag (e, i, false),
4444 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4446 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4447 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4448 physical_screen_height (_editor->get_window())));
4449 _drag_rect->hide ();
4451 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_RangeDragRect());
4452 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_RangeDragRect());
4456 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4458 if (_editor->session() == 0) {
4462 Gdk::Cursor* cursor = 0;
4464 if (!_editor->temp_location) {
4465 _editor->temp_location = new Location (*_editor->session());
4468 switch (_operation) {
4469 case CreateSkipMarker:
4470 case CreateRangeMarker:
4471 case CreateTransportMarker:
4472 case CreateCDMarker:
4474 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4479 cursor = _editor->cursors()->selector;
4483 Drag::start_grab (event, cursor);
4485 show_verbose_cursor_time (adjusted_current_frame (event));
4489 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4491 framepos_t start = 0;
4493 ArdourCanvas::Rectangle *crect;
4495 switch (_operation) {
4496 case CreateSkipMarker:
4497 crect = _editor->range_bar_drag_rect;
4499 case CreateRangeMarker:
4500 crect = _editor->range_bar_drag_rect;
4502 case CreateTransportMarker:
4503 crect = _editor->transport_bar_drag_rect;
4505 case CreateCDMarker:
4506 crect = _editor->cd_marker_bar_drag_rect;
4509 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4514 framepos_t const pf = adjusted_current_frame (event);
4516 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4517 framepos_t grab = grab_frame ();
4518 _editor->snap_to (grab);
4520 if (pf < grab_frame()) {
4528 /* first drag: Either add to the selection
4529 or create a new selection.
4534 _editor->temp_location->set (start, end);
4538 update_item (_editor->temp_location);
4540 //_drag_rect->raise_to_top();
4546 _editor->temp_location->set (start, end);
4548 double x1 = _editor->sample_to_pixel (start);
4549 double x2 = _editor->sample_to_pixel (end);
4553 update_item (_editor->temp_location);
4556 show_verbose_cursor_time (pf);
4561 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4563 Location * newloc = 0;
4567 if (movement_occurred) {
4568 motion (event, false);
4571 switch (_operation) {
4572 case CreateSkipMarker:
4573 case CreateRangeMarker:
4574 case CreateCDMarker:
4576 XMLNode &before = _editor->session()->locations()->get_state();
4577 if (_operation == CreateSkipMarker) {
4578 _editor->begin_reversible_command (_("new skip marker"));
4579 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4580 flags = Location::IsRangeMarker | Location::IsSkip;
4581 _editor->range_bar_drag_rect->hide();
4582 } else if (_operation == CreateCDMarker) {
4583 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4584 _editor->begin_reversible_command (_("new CD marker"));
4585 flags = Location::IsRangeMarker | Location::IsCDMarker;
4586 _editor->cd_marker_bar_drag_rect->hide();
4588 _editor->begin_reversible_command (_("new skip marker"));
4589 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4590 flags = Location::IsRangeMarker;
4591 _editor->range_bar_drag_rect->hide();
4593 newloc = new Location (
4594 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4597 _editor->session()->locations()->add (newloc, true);
4598 XMLNode &after = _editor->session()->locations()->get_state();
4599 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4600 _editor->commit_reversible_command ();
4604 case CreateTransportMarker:
4605 // popup menu to pick loop or punch
4606 _editor->new_transport_marker_context_menu (&event->button, _item);
4612 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4614 if (_operation == CreateTransportMarker) {
4616 /* didn't drag, so just locate */
4618 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4620 } else if (_operation == CreateCDMarker) {
4622 /* didn't drag, but mark is already created so do
4625 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
4631 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4633 if (end == max_framepos) {
4634 end = _editor->session()->current_end_frame ();
4637 if (start == max_framepos) {
4638 start = _editor->session()->current_start_frame ();
4641 switch (_editor->mouse_mode) {
4643 /* find the two markers on either side and then make the selection from it */
4644 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4648 /* find the two markers on either side of the click and make the range out of it */
4649 _editor->selection->set (start, end);
4658 _editor->stop_canvas_autoscroll ();
4662 RangeMarkerBarDrag::aborted (bool)
4668 RangeMarkerBarDrag::update_item (Location* location)
4670 double const x1 = _editor->sample_to_pixel (location->start());
4671 double const x2 = _editor->sample_to_pixel (location->end());
4673 _drag_rect->set_x0 (x1);
4674 _drag_rect->set_x1 (x2);
4677 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4679 , _cumulative_dx (0)
4680 , _cumulative_dy (0)
4682 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4684 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4686 _region = &_primary->region_view ();
4687 _note_height = _region->midi_stream_view()->note_height ();
4691 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4693 Drag::start_grab (event);
4695 if (!(_was_selected = _primary->selected())) {
4697 /* tertiary-click means extend selection - we'll do that on button release,
4698 so don't add it here, because otherwise we make it hard to figure
4699 out the "extend-to" range.
4702 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4705 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4708 _region->note_selected (_primary, true);
4710 _region->unique_select (_primary);
4716 /** @return Current total drag x change in frames */
4718 NoteDrag::total_dx () const
4721 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4723 /* primary note time */
4724 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4726 /* new time of the primary note in session frames */
4727 frameoffset_t st = n + dx;
4729 framepos_t const rp = _region->region()->position ();
4731 /* prevent the note being dragged earlier than the region's position */
4734 /* snap and return corresponding delta */
4735 return _region->snap_frame_to_frame (st - rp) + rp - n;
4738 /** @return Current total drag y change in note number */
4740 NoteDrag::total_dy () const
4742 MidiStreamView* msv = _region->midi_stream_view ();
4743 double const y = _region->midi_view()->y_position ();
4744 /* new current note */
4745 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4747 n = max (msv->lowest_note(), n);
4748 n = min (msv->highest_note(), n);
4749 /* and work out delta */
4750 return n - msv->y_to_note (grab_y() - y);
4754 NoteDrag::motion (GdkEvent *, bool)
4756 /* Total change in x and y since the start of the drag */
4757 frameoffset_t const dx = total_dx ();
4758 int8_t const dy = total_dy ();
4760 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4761 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4762 double const tdy = -dy * _note_height - _cumulative_dy;
4765 _cumulative_dx += tdx;
4766 _cumulative_dy += tdy;
4768 int8_t note_delta = total_dy();
4770 _region->move_selection (tdx, tdy, note_delta);
4772 /* the new note value may be the same as the old one, but we
4773 * don't know what that means because the selection may have
4774 * involved more than one note and we might be doing something
4775 * odd with them. so show the note value anyway, always.
4779 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4781 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4782 (int) floor ((double)new_note));
4784 show_verbose_cursor_text (buf);
4789 NoteDrag::finished (GdkEvent* ev, bool moved)
4792 /* no motion - select note */
4794 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4795 _editor->current_mouse_mode() == Editing::MouseDraw) {
4797 if (_was_selected) {
4798 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4800 _region->note_deselected (_primary);
4803 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4804 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4806 if (!extend && !add && _region->selection_size() > 1) {
4807 _region->unique_select (_primary);
4808 } else if (extend) {
4809 _region->note_selected (_primary, true, true);
4811 /* it was added during button press */
4816 _region->note_dropped (_primary, total_dx(), total_dy());
4821 NoteDrag::aborted (bool)
4826 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4827 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4828 : Drag (editor, atv->base_item ())
4830 , _nothing_to_drag (false)
4832 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4833 y_origin = atv->y_position();
4834 setup (atv->lines ());
4837 /** Make an AutomationRangeDrag for region gain lines */
4838 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4839 : Drag (editor, rv->get_canvas_group ())
4841 , _nothing_to_drag (false)
4843 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4845 list<boost::shared_ptr<AutomationLine> > lines;
4846 lines.push_back (rv->get_gain_line ());
4847 y_origin = rv->get_time_axis_view().y_position();
4851 /** @param lines AutomationLines to drag.
4852 * @param offset Offset from the session start to the points in the AutomationLines.
4855 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4857 /* find the lines that overlap the ranges being dragged */
4858 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4859 while (i != lines.end ()) {
4860 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4863 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4865 /* check this range against all the AudioRanges that we are using */
4866 list<AudioRange>::const_iterator k = _ranges.begin ();
4867 while (k != _ranges.end()) {
4868 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4874 /* add it to our list if it overlaps at all */
4875 if (k != _ranges.end()) {
4880 _lines.push_back (n);
4886 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4890 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4892 return 1.0 - ((global_y - y_origin) / line->height());
4896 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4898 Drag::start_grab (event, cursor);
4900 /* Get line states before we start changing things */
4901 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4902 i->state = &i->line->get_state ();
4903 i->original_fraction = y_fraction (i->line, current_pointer_y());
4906 if (_ranges.empty()) {
4908 /* No selected time ranges: drag all points */
4909 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4910 uint32_t const N = i->line->npoints ();
4911 for (uint32_t j = 0; j < N; ++j) {
4912 i->points.push_back (i->line->nth (j));
4918 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4920 framecnt_t const half = (i->start + i->end) / 2;
4922 /* find the line that this audio range starts in */
4923 list<Line>::iterator j = _lines.begin();
4924 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4928 if (j != _lines.end()) {
4929 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4931 /* j is the line that this audio range starts in; fade into it;
4932 64 samples length plucked out of thin air.
4935 framepos_t a = i->start + 64;
4940 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4941 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4943 the_list->editor_add (p, the_list->eval (p));
4944 the_list->editor_add (q, the_list->eval (q));
4947 /* same thing for the end */
4950 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4954 if (j != _lines.end()) {
4955 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4957 /* j is the line that this audio range starts in; fade out of it;
4958 64 samples length plucked out of thin air.
4961 framepos_t b = i->end - 64;
4966 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4967 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4969 the_list->editor_add (p, the_list->eval (p));
4970 the_list->editor_add (q, the_list->eval (q));
4974 _nothing_to_drag = true;
4976 /* Find all the points that should be dragged and put them in the relevant
4977 points lists in the Line structs.
4980 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4982 uint32_t const N = i->line->npoints ();
4983 for (uint32_t j = 0; j < N; ++j) {
4985 /* here's a control point on this line */
4986 ControlPoint* p = i->line->nth (j);
4987 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4989 /* see if it's inside a range */
4990 list<AudioRange>::const_iterator k = _ranges.begin ();
4991 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4995 if (k != _ranges.end()) {
4996 /* dragging this point */
4997 _nothing_to_drag = false;
4998 i->points.push_back (p);
5004 if (_nothing_to_drag) {
5008 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5009 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5014 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5016 if (_nothing_to_drag) {
5020 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5021 float const f = y_fraction (l->line, current_pointer_y());
5022 /* we are ignoring x position for this drag, so we can just pass in anything */
5024 l->line->drag_motion (0, f, true, false, ignored);
5025 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5030 AutomationRangeDrag::finished (GdkEvent* event, bool)
5032 if (_nothing_to_drag) {
5036 motion (event, false);
5037 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5038 i->line->end_drag (false, 0);
5041 _editor->session()->commit_reversible_command ();
5045 AutomationRangeDrag::aborted (bool)
5047 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5052 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5054 , initial_time_axis_view (itav)
5056 /* note that time_axis_view may be null if the regionview was created
5057 * as part of a copy operation.
5059 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5060 layer = v->region()->layer ();
5061 initial_y = v->get_canvas_group()->position().y;
5062 initial_playlist = v->region()->playlist ();
5063 initial_position = v->region()->position ();
5064 initial_end = v->region()->position () + v->region()->length ();
5067 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5068 : Drag (e, i->canvas_item ())
5071 , _cumulative_dx (0)
5073 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5074 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5079 PatchChangeDrag::motion (GdkEvent* ev, bool)
5081 framepos_t f = adjusted_current_frame (ev);
5082 boost::shared_ptr<Region> r = _region_view->region ();
5083 f = max (f, r->position ());
5084 f = min (f, r->last_frame ());
5086 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5087 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5088 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5089 _cumulative_dx = dxu;
5093 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5095 if (!movement_occurred) {
5099 boost::shared_ptr<Region> r (_region_view->region ());
5100 framepos_t f = adjusted_current_frame (ev);
5101 f = max (f, r->position ());
5102 f = min (f, r->last_frame ());
5104 _region_view->move_patch_change (
5106 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5111 PatchChangeDrag::aborted (bool)
5113 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5117 PatchChangeDrag::setup_pointer_frame_offset ()
5119 boost::shared_ptr<Region> region = _region_view->region ();
5120 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5123 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5124 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5131 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5133 _region_view->update_drag_selection (
5135 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5139 MidiRubberbandSelectDrag::deselect_things ()
5144 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5145 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5148 _vertical_only = true;
5152 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5154 double const y = _region_view->midi_view()->y_position ();
5156 y1 = max (0.0, y1 - y);
5157 y2 = max (0.0, y2 - y);
5159 _region_view->update_vertical_drag_selection (
5162 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5167 MidiVerticalSelectDrag::deselect_things ()
5172 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5173 : RubberbandSelectDrag (e, i)
5179 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5181 if (drag_in_progress) {
5182 /* We just want to select things at the end of the drag, not during it */
5186 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5188 _editor->begin_reversible_command (_("rubberband selection"));
5189 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5190 _editor->commit_reversible_command ();
5194 EditorRubberbandSelectDrag::deselect_things ()
5196 if (!getenv("ARDOUR_SAE")) {
5197 _editor->selection->clear_tracks();
5199 _editor->selection->clear_regions();
5200 _editor->selection->clear_points ();
5201 _editor->selection->clear_lines ();
5204 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5212 NoteCreateDrag::~NoteCreateDrag ()
5218 NoteCreateDrag::grid_frames (framepos_t t) const
5221 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
5223 grid_beats = Evoral::MusicalTime(1);
5226 return _region_view->region_beats_to_region_frames (grid_beats);
5230 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5232 Drag::start_grab (event, cursor);
5234 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5236 framepos_t pf = _drags->current_pointer_frame ();
5237 framecnt_t const g = grid_frames (pf);
5239 /* Hack so that we always snap to the note that we are over, instead of snapping
5240 to the next one if we're more than halfway through the one we're over.
5242 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5246 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5248 MidiStreamView* sv = _region_view->midi_stream_view ();
5249 double const x = _editor->sample_to_pixel (_note[0]);
5250 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5252 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5253 _drag_rect->set_outline_all ();
5254 _drag_rect->set_outline_color (0xffffff99);
5255 _drag_rect->set_fill_color (0xffffff66);
5259 NoteCreateDrag::motion (GdkEvent* event, bool)
5261 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5262 double const x0 = _editor->sample_to_pixel (_note[0]);
5263 double const x1 = _editor->sample_to_pixel (_note[1]);
5264 _drag_rect->set_x0 (std::min(x0, x1));
5265 _drag_rect->set_x1 (std::max(x0, x1));
5269 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5271 if (!had_movement) {
5275 framepos_t const start = min (_note[0], _note[1]);
5276 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5278 framecnt_t const g = grid_frames (start);
5279 Evoral::MusicalTime const one_tick = Evoral::MusicalTime::ticks(1);
5281 if (_editor->snap_mode() == SnapNormal && length < g) {
5285 Evoral::MusicalTime length_beats = max (
5286 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5288 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5292 NoteCreateDrag::y_to_region (double y) const
5295 _region_view->get_canvas_group()->canvas_to_item (x, y);
5300 NoteCreateDrag::aborted (bool)
5305 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5310 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5314 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5316 Drag::start_grab (event, cursor);
5320 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5326 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5329 distance = _drags->current_pointer_x() - grab_x();
5330 len = ar->fade_in()->back()->when;
5332 distance = grab_x() - _drags->current_pointer_x();
5333 len = ar->fade_out()->back()->when;
5336 /* how long should it be ? */
5338 new_length = len + _editor->pixel_to_sample (distance);
5340 /* now check with the region that this is legal */
5342 new_length = ar->verify_xfade_bounds (new_length, start);
5345 arv->reset_fade_in_shape_width (ar, new_length);
5347 arv->reset_fade_out_shape_width (ar, new_length);
5352 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5358 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5361 distance = _drags->current_pointer_x() - grab_x();
5362 len = ar->fade_in()->back()->when;
5364 distance = grab_x() - _drags->current_pointer_x();
5365 len = ar->fade_out()->back()->when;
5368 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5370 _editor->begin_reversible_command ("xfade trim");
5371 ar->playlist()->clear_owned_changes ();
5374 ar->set_fade_in_length (new_length);
5376 ar->set_fade_out_length (new_length);
5379 /* Adjusting the xfade may affect other regions in the playlist, so we need
5380 to get undo Commands from the whole playlist rather than just the
5384 vector<Command*> cmds;
5385 ar->playlist()->rdiff (cmds);
5386 _editor->session()->add_commands (cmds);
5387 _editor->commit_reversible_command ();
5392 CrossfadeEdgeDrag::aborted (bool)
5395 arv->redraw_start_xfade ();
5397 arv->redraw_end_xfade ();
5401 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5402 : Drag (e, item, true)
5403 , line (new EditorCursor (*e))
5405 line->set_position (pos);
5409 RegionCutDrag::~RegionCutDrag ()
5415 RegionCutDrag::motion (GdkEvent*, bool)
5417 framepos_t where = _drags->current_pointer_frame();
5418 _editor->snap_to (where);
5420 line->set_position (where);
5424 RegionCutDrag::finished (GdkEvent*, bool)
5426 _editor->get_track_canvas()->canvas()->re_enter();
5428 framepos_t pos = _drags->current_pointer_frame();
5432 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5438 _editor->split_regions_at (pos, rs);
5442 RegionCutDrag::aborted (bool)