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;
1928 cursor = _editor->cursors()->right_side_trim;
1931 Drag::start_grab (event, cursor);
1933 region = &cnote->region_view();
1935 double const region_start = region->get_position_pixels();
1936 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1938 if (grab_x() <= middle_point) {
1939 cursor = _editor->cursors()->left_side_trim;
1942 cursor = _editor->cursors()->right_side_trim;
1948 if (event->motion.state & Keyboard::PrimaryModifier) {
1954 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1956 if (ms.size() > 1) {
1957 /* has to be relative, may make no sense otherwise */
1961 /* select this note; if it is already selected, preserve the existing selection,
1962 otherwise make this note the only one selected.
1964 region->note_selected (cnote, cnote->selected ());
1966 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1967 MidiRegionSelection::iterator next;
1970 (*r)->begin_resizing (at_front);
1976 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1978 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1979 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1980 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1982 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1987 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1989 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1990 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1991 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1993 (*r)->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 (*r)->abort_resizing ();
2006 AVDraggingView::AVDraggingView (RegionView* v)
2009 initial_position = v->region()->position ();
2012 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2015 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2018 TrackViewList empty;
2020 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2021 std::list<RegionView*> views = rs.by_layer();
2023 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2024 RegionView* rv = (*i);
2025 if (!rv->region()->video_locked()) {
2028 _views.push_back (AVDraggingView (rv));
2033 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2035 Drag::start_grab (event);
2036 if (_editor->session() == 0) {
2040 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2041 _max_backwards_drag = (
2042 ARDOUR_UI::instance()->video_timeline->get_duration()
2043 + ARDOUR_UI::instance()->video_timeline->get_offset()
2044 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2047 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2048 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2049 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2052 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2055 Timecode::Time timecode;
2056 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2057 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);
2058 show_verbose_cursor_text (buf);
2062 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2064 if (_editor->session() == 0) {
2067 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2071 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2072 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2074 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2075 dt = - _max_backwards_drag;
2078 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2079 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2081 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2082 RegionView* rv = i->view;
2083 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2086 rv->region()->clear_changes ();
2087 rv->region()->suspend_property_changes();
2089 rv->region()->set_position(i->initial_position + dt);
2090 rv->region_changed(ARDOUR::Properties::position);
2093 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2094 Timecode::Time timecode;
2095 Timecode::Time timediff;
2097 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2098 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2099 snprintf (buf, sizeof (buf),
2100 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2101 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2102 , _("Video Start:"),
2103 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2105 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2107 show_verbose_cursor_text (buf);
2111 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2113 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2117 if (!movement_occurred || ! _editor->session()) {
2121 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2123 _editor->begin_reversible_command (_("Move Video"));
2125 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2126 ARDOUR_UI::instance()->video_timeline->save_undo();
2127 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2128 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2130 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2131 i->view->drag_end();
2132 i->view->region()->resume_property_changes ();
2134 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2137 _editor->session()->maybe_update_session_range(
2138 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2139 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2143 _editor->commit_reversible_command ();
2147 VideoTimeLineDrag::aborted (bool)
2149 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2152 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2153 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2155 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2156 i->view->region()->resume_property_changes ();
2157 i->view->region()->set_position(i->initial_position);
2161 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2162 : RegionDrag (e, i, p, v)
2163 , _preserve_fade_anchor (preserve_fade_anchor)
2164 , _jump_position_when_done (false)
2166 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2170 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2173 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2174 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2176 if (tv && tv->is_track()) {
2177 speed = tv->track()->speed();
2180 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2181 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2182 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2184 framepos_t const pf = adjusted_current_frame (event);
2186 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2187 /* Move the contents of the region around without changing the region bounds */
2188 _operation = ContentsTrim;
2189 Drag::start_grab (event, _editor->cursors()->trimmer);
2191 /* These will get overridden for a point trim.*/
2192 if (pf < (region_start + region_length/2)) {
2193 /* closer to front */
2194 _operation = StartTrim;
2196 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2197 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2199 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2203 _operation = EndTrim;
2204 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2205 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2207 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2212 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2213 _jump_position_when_done = true;
2216 switch (_operation) {
2218 show_verbose_cursor_time (region_start);
2219 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2220 i->view->trim_front_starting ();
2224 show_verbose_cursor_time (region_end);
2227 show_verbose_cursor_time (pf);
2231 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2232 i->view->region()->suspend_property_changes ();
2237 TrimDrag::motion (GdkEvent* event, bool first_move)
2239 RegionView* rv = _primary;
2242 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2243 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2244 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2245 frameoffset_t frame_delta = 0;
2247 if (tv && tv->is_track()) {
2248 speed = tv->track()->speed();
2251 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2257 switch (_operation) {
2259 trim_type = "Region start trim";
2262 trim_type = "Region end trim";
2265 trim_type = "Region content trim";
2272 _editor->begin_reversible_command (trim_type);
2274 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2275 RegionView* rv = i->view;
2276 rv->enable_display (false);
2277 rv->region()->playlist()->clear_owned_changes ();
2279 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2282 arv->temporarily_hide_envelope ();
2286 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2287 insert_result = _editor->motion_frozen_playlists.insert (pl);
2289 if (insert_result.second) {
2295 bool non_overlap_trim = false;
2297 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2298 non_overlap_trim = true;
2301 /* contstrain trim to fade length */
2302 if (_preserve_fade_anchor) {
2303 switch (_operation) {
2305 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2306 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2308 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2309 if (ar->locked()) continue;
2310 framecnt_t len = ar->fade_in()->back()->when;
2311 if (len < dt) dt = min(dt, len);
2315 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2316 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2318 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2319 if (ar->locked()) continue;
2320 framecnt_t len = ar->fade_out()->back()->when;
2321 if (len < -dt) dt = max(dt, -len);
2330 switch (_operation) {
2332 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2333 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2334 if (changed && _preserve_fade_anchor) {
2335 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2337 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2338 framecnt_t len = ar->fade_in()->back()->when;
2339 framecnt_t diff = ar->first_frame() - i->initial_position;
2340 framepos_t new_length = len - diff;
2341 i->anchored_fade_length = min (ar->length(), new_length);
2342 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2343 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2350 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2351 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2352 if (changed && _preserve_fade_anchor) {
2353 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2355 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2356 framecnt_t len = ar->fade_out()->back()->when;
2357 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2358 framepos_t new_length = len + diff;
2359 i->anchored_fade_length = min (ar->length(), new_length);
2360 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2361 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2369 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2371 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2372 i->view->move_contents (frame_delta);
2378 switch (_operation) {
2380 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2383 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2386 // show_verbose_cursor_time (frame_delta);
2393 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2395 if (movement_occurred) {
2396 motion (event, false);
2398 if (_operation == StartTrim) {
2399 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2401 /* This must happen before the region's StatefulDiffCommand is created, as it may
2402 `correct' (ahem) the region's _start from being negative to being zero. It
2403 needs to be zero in the undo record.
2405 i->view->trim_front_ending ();
2407 if (_preserve_fade_anchor) {
2408 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2410 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2411 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2412 ar->set_fade_in_length(i->anchored_fade_length);
2413 ar->set_fade_in_active(true);
2416 if (_jump_position_when_done) {
2417 i->view->region()->set_position (i->initial_position);
2420 } else if (_operation == EndTrim) {
2421 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2422 if (_preserve_fade_anchor) {
2423 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2425 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2426 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2427 ar->set_fade_out_length(i->anchored_fade_length);
2428 ar->set_fade_out_active(true);
2431 if (_jump_position_when_done) {
2432 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2437 if (!_views.empty()) {
2438 if (_operation == StartTrim) {
2439 _editor->maybe_locate_with_edit_preroll(
2440 _views.begin()->view->region()->position());
2442 if (_operation == EndTrim) {
2443 _editor->maybe_locate_with_edit_preroll(
2444 _views.begin()->view->region()->position() +
2445 _views.begin()->view->region()->length());
2449 if (!_editor->selection->selected (_primary)) {
2450 _primary->thaw_after_trim ();
2453 set<boost::shared_ptr<Playlist> > diffed_playlists;
2455 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2456 i->view->thaw_after_trim ();
2457 i->view->enable_display (true);
2459 /* Trimming one region may affect others on the playlist, so we need
2460 to get undo Commands from the whole playlist rather than just the
2461 region. Use diffed_playlists to make sure we don't diff a given
2462 playlist more than once.
2464 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2465 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2466 vector<Command*> cmds;
2468 _editor->session()->add_commands (cmds);
2469 diffed_playlists.insert (p);
2474 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2478 _editor->motion_frozen_playlists.clear ();
2479 _editor->commit_reversible_command();
2482 /* no mouse movement */
2483 _editor->point_trim (event, adjusted_current_frame (event));
2486 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2487 if (_operation == StartTrim) {
2488 i->view->trim_front_ending ();
2491 i->view->region()->resume_property_changes ();
2496 TrimDrag::aborted (bool movement_occurred)
2498 /* Our motion method is changing model state, so use the Undo system
2499 to cancel. Perhaps not ideal, as this will leave an Undo point
2500 behind which may be slightly odd from the user's point of view.
2505 if (movement_occurred) {
2509 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2510 i->view->region()->resume_property_changes ();
2515 TrimDrag::setup_pointer_frame_offset ()
2517 list<DraggingView>::iterator i = _views.begin ();
2518 while (i != _views.end() && i->view != _primary) {
2522 if (i == _views.end()) {
2526 switch (_operation) {
2528 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2531 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2538 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2542 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2543 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2548 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2550 Drag::start_grab (event, cursor);
2551 show_verbose_cursor_time (adjusted_current_frame(event));
2555 MeterMarkerDrag::setup_pointer_frame_offset ()
2557 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2561 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2563 if (!_marker->meter().movable()) {
2569 // create a dummy marker for visual representation of moving the
2570 // section, because whether its a copy or not, we're going to
2571 // leave or lose the original marker (leave if its a copy; lose if its
2572 // not, because we'll remove it from the map).
2574 MeterSection section (_marker->meter());
2576 if (!section.movable()) {
2581 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2583 _marker = new MeterMarker (
2585 *_editor->meter_group,
2586 ARDOUR_UI::config()->get_MeterMarker(),
2588 *new MeterSection (_marker->meter())
2591 /* use the new marker for the grab */
2592 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2595 TempoMap& map (_editor->session()->tempo_map());
2596 /* get current state */
2597 before_state = &map.get_state();
2598 /* remove the section while we drag it */
2599 map.remove_meter (section, true);
2603 framepos_t const pf = adjusted_current_frame (event);
2604 _marker->set_position (pf);
2605 show_verbose_cursor_time (pf);
2609 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2611 if (!movement_occurred) {
2612 if (was_double_click()) {
2613 _editor->edit_meter_marker (*_marker);
2618 if (!_marker->meter().movable()) {
2622 motion (event, false);
2624 Timecode::BBT_Time when;
2626 TempoMap& map (_editor->session()->tempo_map());
2627 map.bbt_time (last_pointer_frame(), when);
2629 if (_copy == true) {
2630 _editor->begin_reversible_command (_("copy meter mark"));
2631 XMLNode &before = map.get_state();
2632 map.add_meter (_marker->meter(), when);
2633 XMLNode &after = map.get_state();
2634 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2635 _editor->commit_reversible_command ();
2638 _editor->begin_reversible_command (_("move meter mark"));
2640 /* we removed it before, so add it back now */
2642 map.add_meter (_marker->meter(), when);
2643 XMLNode &after = map.get_state();
2644 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2645 _editor->commit_reversible_command ();
2648 // delete the dummy marker we used for visual representation while moving.
2649 // a new visual marker will show up automatically.
2654 MeterMarkerDrag::aborted (bool moved)
2656 _marker->set_position (_marker->meter().frame ());
2659 TempoMap& map (_editor->session()->tempo_map());
2660 /* we removed it before, so add it back now */
2661 map.add_meter (_marker->meter(), _marker->meter().frame());
2662 // delete the dummy marker we used for visual representation while moving.
2663 // a new visual marker will show up automatically.
2668 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2672 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2674 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2679 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2681 Drag::start_grab (event, cursor);
2682 show_verbose_cursor_time (adjusted_current_frame (event));
2686 TempoMarkerDrag::setup_pointer_frame_offset ()
2688 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2692 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2694 if (!_marker->tempo().movable()) {
2700 // create a dummy marker for visual representation of moving the
2701 // section, because whether its a copy or not, we're going to
2702 // leave or lose the original marker (leave if its a copy; lose if its
2703 // not, because we'll remove it from the map).
2705 // create a dummy marker for visual representation of moving the copy.
2706 // The actual copying is not done before we reach the finish callback.
2709 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2711 TempoSection section (_marker->tempo());
2713 _marker = new TempoMarker (
2715 *_editor->tempo_group,
2716 ARDOUR_UI::config()->get_TempoMarker(),
2718 *new TempoSection (_marker->tempo())
2721 /* use the new marker for the grab */
2722 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2725 TempoMap& map (_editor->session()->tempo_map());
2726 /* get current state */
2727 before_state = &map.get_state();
2728 /* remove the section while we drag it */
2729 map.remove_tempo (section, true);
2733 framepos_t const pf = adjusted_current_frame (event);
2734 _marker->set_position (pf);
2735 show_verbose_cursor_time (pf);
2739 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2741 if (!movement_occurred) {
2742 if (was_double_click()) {
2743 _editor->edit_tempo_marker (*_marker);
2748 if (!_marker->tempo().movable()) {
2752 motion (event, false);
2754 TempoMap& map (_editor->session()->tempo_map());
2755 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2756 Timecode::BBT_Time when;
2758 map.bbt_time (beat_time, when);
2760 if (_copy == true) {
2761 _editor->begin_reversible_command (_("copy tempo mark"));
2762 XMLNode &before = map.get_state();
2763 map.add_tempo (_marker->tempo(), when);
2764 XMLNode &after = map.get_state();
2765 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2766 _editor->commit_reversible_command ();
2769 _editor->begin_reversible_command (_("move tempo mark"));
2770 /* we removed it before, so add it back now */
2771 map.add_tempo (_marker->tempo(), when);
2772 XMLNode &after = map.get_state();
2773 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2774 _editor->commit_reversible_command ();
2777 // delete the dummy marker we used for visual representation while moving.
2778 // a new visual marker will show up automatically.
2783 TempoMarkerDrag::aborted (bool moved)
2785 _marker->set_position (_marker->tempo().frame());
2787 TempoMap& map (_editor->session()->tempo_map());
2788 /* we removed it before, so add it back now */
2789 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2790 // delete the dummy marker we used for visual representation while moving.
2791 // a new visual marker will show up automatically.
2796 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2797 : Drag (e, &c.track_canvas_item(), false)
2801 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2804 /** Do all the things we do when dragging the playhead to make it look as though
2805 * we have located, without actually doing the locate (because that would cause
2806 * the diskstream buffers to be refilled, which is too slow).
2809 CursorDrag::fake_locate (framepos_t t)
2811 _editor->playhead_cursor->set_position (t);
2813 Session* s = _editor->session ();
2814 if (s->timecode_transmission_suspended ()) {
2815 framepos_t const f = _editor->playhead_cursor->current_frame ();
2816 /* This is asynchronous so it will be sent "now"
2818 s->send_mmc_locate (f);
2819 /* These are synchronous and will be sent during the next
2822 s->queue_full_time_code ();
2823 s->queue_song_position_pointer ();
2826 show_verbose_cursor_time (t);
2827 _editor->UpdateAllTransportClocks (t);
2831 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2833 Drag::start_grab (event, c);
2835 _grab_zoom = _editor->samples_per_pixel;
2837 framepos_t where = _editor->canvas_event_sample (event);
2839 _editor->snap_to_with_modifier (where, event);
2841 _editor->_dragging_playhead = true;
2843 Session* s = _editor->session ();
2845 /* grab the track canvas item as well */
2847 _cursor.track_canvas_item().grab();
2850 if (_was_rolling && _stop) {
2854 if (s->is_auditioning()) {
2855 s->cancel_audition ();
2859 if (AudioEngine::instance()->connected()) {
2861 /* do this only if we're the engine is connected
2862 * because otherwise this request will never be
2863 * serviced and we'll busy wait forever. likewise,
2864 * notice if we are disconnected while waiting for the
2865 * request to be serviced.
2868 s->request_suspend_timecode_transmission ();
2869 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2870 /* twiddle our thumbs */
2875 fake_locate (where);
2879 CursorDrag::motion (GdkEvent* event, bool)
2881 framepos_t const adjusted_frame = adjusted_current_frame (event);
2882 if (adjusted_frame != last_pointer_frame()) {
2883 fake_locate (adjusted_frame);
2888 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2890 _editor->_dragging_playhead = false;
2892 _cursor.track_canvas_item().ungrab();
2894 if (!movement_occurred && _stop) {
2898 motion (event, false);
2900 Session* s = _editor->session ();
2902 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2903 _editor->_pending_locate_request = true;
2904 s->request_resume_timecode_transmission ();
2909 CursorDrag::aborted (bool)
2911 _cursor.track_canvas_item().ungrab();
2913 if (_editor->_dragging_playhead) {
2914 _editor->session()->request_resume_timecode_transmission ();
2915 _editor->_dragging_playhead = false;
2918 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2921 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2922 : RegionDrag (e, i, p, v)
2924 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2928 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2930 Drag::start_grab (event, cursor);
2932 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2933 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2935 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2939 FadeInDrag::setup_pointer_frame_offset ()
2941 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2942 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2943 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2947 FadeInDrag::motion (GdkEvent* event, bool)
2949 framecnt_t fade_length;
2951 framepos_t const pos = adjusted_current_frame (event);
2953 boost::shared_ptr<Region> region = _primary->region ();
2955 if (pos < (region->position() + 64)) {
2956 fade_length = 64; // this should be a minimum defined somewhere
2957 } else if (pos > region->last_frame()) {
2958 fade_length = region->length();
2960 fade_length = pos - region->position();
2963 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2965 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2971 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2974 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2978 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2980 if (!movement_occurred) {
2984 framecnt_t fade_length;
2986 framepos_t const pos = adjusted_current_frame (event);
2988 boost::shared_ptr<Region> region = _primary->region ();
2990 if (pos < (region->position() + 64)) {
2991 fade_length = 64; // this should be a minimum defined somewhere
2992 } else if (pos > region->last_frame()) {
2993 fade_length = region->length();
2995 fade_length = pos - region->position();
2998 _editor->begin_reversible_command (_("change fade in length"));
3000 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3002 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3008 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3009 XMLNode &before = alist->get_state();
3011 tmp->audio_region()->set_fade_in_length (fade_length);
3012 tmp->audio_region()->set_fade_in_active (true);
3014 XMLNode &after = alist->get_state();
3015 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3018 _editor->commit_reversible_command ();
3022 FadeInDrag::aborted (bool)
3024 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3025 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3031 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3035 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3036 : RegionDrag (e, i, p, v)
3038 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3042 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3044 Drag::start_grab (event, cursor);
3046 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3047 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3049 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3053 FadeOutDrag::setup_pointer_frame_offset ()
3055 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3056 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3057 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3061 FadeOutDrag::motion (GdkEvent* event, bool)
3063 framecnt_t fade_length;
3065 framepos_t const pos = adjusted_current_frame (event);
3067 boost::shared_ptr<Region> region = _primary->region ();
3069 if (pos > (region->last_frame() - 64)) {
3070 fade_length = 64; // this should really be a minimum fade defined somewhere
3072 else if (pos < region->position()) {
3073 fade_length = region->length();
3076 fade_length = region->last_frame() - pos;
3079 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3081 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3087 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3090 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3094 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3096 if (!movement_occurred) {
3100 framecnt_t fade_length;
3102 framepos_t const pos = adjusted_current_frame (event);
3104 boost::shared_ptr<Region> region = _primary->region ();
3106 if (pos > (region->last_frame() - 64)) {
3107 fade_length = 64; // this should really be a minimum fade defined somewhere
3109 else if (pos < region->position()) {
3110 fade_length = region->length();
3113 fade_length = region->last_frame() - pos;
3116 _editor->begin_reversible_command (_("change fade out length"));
3118 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3120 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3126 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3127 XMLNode &before = alist->get_state();
3129 tmp->audio_region()->set_fade_out_length (fade_length);
3130 tmp->audio_region()->set_fade_out_active (true);
3132 XMLNode &after = alist->get_state();
3133 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3136 _editor->commit_reversible_command ();
3140 FadeOutDrag::aborted (bool)
3142 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3143 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3149 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3153 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3156 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3158 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3161 _points.push_back (ArdourCanvas::Duple (0, 0));
3162 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3165 MarkerDrag::~MarkerDrag ()
3167 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3172 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3174 location = new Location (*l);
3175 markers.push_back (m);
3180 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3182 Drag::start_grab (event, cursor);
3186 Location *location = _editor->find_location_from_marker (_marker, is_start);
3187 _editor->_dragging_edit_point = true;
3189 update_item (location);
3191 // _drag_line->show();
3192 // _line->raise_to_top();
3195 show_verbose_cursor_time (location->start());
3197 show_verbose_cursor_time (location->end());
3200 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3203 case Selection::Toggle:
3204 /* we toggle on the button release */
3206 case Selection::Set:
3207 if (!_editor->selection->selected (_marker)) {
3208 _editor->selection->set (_marker);
3211 case Selection::Extend:
3213 Locations::LocationList ll;
3214 list<Marker*> to_add;
3216 _editor->selection->markers.range (s, e);
3217 s = min (_marker->position(), s);
3218 e = max (_marker->position(), e);
3221 if (e < max_framepos) {
3224 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3225 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3226 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3229 to_add.push_back (lm->start);
3232 to_add.push_back (lm->end);
3236 if (!to_add.empty()) {
3237 _editor->selection->add (to_add);
3241 case Selection::Add:
3242 _editor->selection->add (_marker);
3246 /* Set up copies for us to manipulate during the drag
3249 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3251 Location* l = _editor->find_location_from_marker (*i, is_start);
3258 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3260 /* range: check that the other end of the range isn't
3263 CopiedLocationInfo::iterator x;
3264 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3265 if (*(*x).location == *l) {
3269 if (x == _copied_locations.end()) {
3270 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3272 (*x).markers.push_back (*i);
3273 (*x).move_both = true;
3281 MarkerDrag::setup_pointer_frame_offset ()
3284 Location *location = _editor->find_location_from_marker (_marker, is_start);
3285 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3289 MarkerDrag::motion (GdkEvent* event, bool)
3291 framecnt_t f_delta = 0;
3293 bool move_both = false;
3294 Location *real_location;
3295 Location *copy_location = 0;
3297 framepos_t const newframe = adjusted_current_frame (event);
3298 framepos_t next = newframe;
3300 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3304 CopiedLocationInfo::iterator x;
3306 /* find the marker we're dragging, and compute the delta */
3308 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3310 copy_location = (*x).location;
3312 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3314 /* this marker is represented by this
3315 * CopiedLocationMarkerInfo
3318 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3323 if (real_location->is_mark()) {
3324 f_delta = newframe - copy_location->start();
3328 switch (_marker->type()) {
3329 case Marker::SessionStart:
3330 case Marker::RangeStart:
3331 case Marker::LoopStart:
3332 case Marker::PunchIn:
3333 f_delta = newframe - copy_location->start();
3336 case Marker::SessionEnd:
3337 case Marker::RangeEnd:
3338 case Marker::LoopEnd:
3339 case Marker::PunchOut:
3340 f_delta = newframe - copy_location->end();
3343 /* what kind of marker is this ? */
3352 if (x == _copied_locations.end()) {
3353 /* hmm, impossible - we didn't find the dragged marker */
3357 /* now move them all */
3359 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3361 copy_location = x->location;
3363 /* call this to find out if its the start or end */
3365 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3369 if (real_location->locked()) {
3373 if (copy_location->is_mark()) {
3377 copy_location->set_start (copy_location->start() + f_delta);
3381 framepos_t new_start = copy_location->start() + f_delta;
3382 framepos_t new_end = copy_location->end() + f_delta;
3384 if (is_start) { // start-of-range marker
3386 if (move_both || (*x).move_both) {
3387 copy_location->set_start (new_start);
3388 copy_location->set_end (new_end);
3389 } else if (new_start < copy_location->end()) {
3390 copy_location->set_start (new_start);
3391 } else if (newframe > 0) {
3392 _editor->snap_to (next, 1, true);
3393 copy_location->set_end (next);
3394 copy_location->set_start (newframe);
3397 } else { // end marker
3399 if (move_both || (*x).move_both) {
3400 copy_location->set_end (new_end);
3401 copy_location->set_start (new_start);
3402 } else if (new_end > copy_location->start()) {
3403 copy_location->set_end (new_end);
3404 } else if (newframe > 0) {
3405 _editor->snap_to (next, -1, true);
3406 copy_location->set_start (next);
3407 copy_location->set_end (newframe);
3412 update_item (copy_location);
3414 /* now lookup the actual GUI items used to display this
3415 * location and move them to wherever the copy of the location
3416 * is now. This means that the logic in ARDOUR::Location is
3417 * still enforced, even though we are not (yet) modifying
3418 * the real Location itself.
3421 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3424 lm->set_position (copy_location->start(), copy_location->end());
3429 assert (!_copied_locations.empty());
3431 show_verbose_cursor_time (newframe);
3435 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3437 if (!movement_occurred) {
3439 if (was_double_click()) {
3440 _editor->rename_marker (_marker);
3444 /* just a click, do nothing but finish
3445 off the selection process
3448 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3451 case Selection::Set:
3452 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3453 _editor->selection->set (_marker);
3457 case Selection::Toggle:
3458 /* we toggle on the button release, click only */
3459 _editor->selection->toggle (_marker);
3462 case Selection::Extend:
3463 case Selection::Add:
3470 _editor->_dragging_edit_point = false;
3472 _editor->begin_reversible_command ( _("move marker") );
3473 XMLNode &before = _editor->session()->locations()->get_state();
3475 MarkerSelection::iterator i;
3476 CopiedLocationInfo::iterator x;
3479 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3480 x != _copied_locations.end() && i != _editor->selection->markers.end();
3483 Location * location = _editor->find_location_from_marker (*i, is_start);
3487 if (location->locked()) {
3491 if (location->is_mark()) {
3492 location->set_start (((*x).location)->start());
3494 location->set (((*x).location)->start(), ((*x).location)->end());
3499 XMLNode &after = _editor->session()->locations()->get_state();
3500 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3501 _editor->commit_reversible_command ();
3505 MarkerDrag::aborted (bool)
3511 MarkerDrag::update_item (Location*)
3516 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3518 _cumulative_x_drag (0),
3519 _cumulative_y_drag (0)
3521 if (_zero_gain_fraction < 0.0) {
3522 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3525 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3527 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3533 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3535 Drag::start_grab (event, _editor->cursors()->fader);
3537 // start the grab at the center of the control point so
3538 // the point doesn't 'jump' to the mouse after the first drag
3539 _fixed_grab_x = _point->get_x();
3540 _fixed_grab_y = _point->get_y();
3542 float const fraction = 1 - (_point->get_y() / _point->line().height());
3544 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3546 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3548 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3550 if (!_point->can_slide ()) {
3551 _x_constrained = true;
3556 ControlPointDrag::motion (GdkEvent* event, bool)
3558 double dx = _drags->current_pointer_x() - last_pointer_x();
3559 double dy = current_pointer_y() - last_pointer_y();
3561 if (event->button.state & Keyboard::SecondaryModifier) {
3566 /* coordinate in pixels relative to the start of the region (for region-based automation)
3567 or track (for track-based automation) */
3568 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3569 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3571 // calculate zero crossing point. back off by .01 to stay on the
3572 // positive side of zero
3573 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3575 // make sure we hit zero when passing through
3576 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3580 if (_x_constrained) {
3583 if (_y_constrained) {
3587 _cumulative_x_drag = cx - _fixed_grab_x;
3588 _cumulative_y_drag = cy - _fixed_grab_y;
3592 cy = min ((double) _point->line().height(), cy);
3594 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3596 if (!_x_constrained) {
3597 _editor->snap_to_with_modifier (cx_frames, event);
3600 cx_frames = min (cx_frames, _point->line().maximum_time());
3602 float const fraction = 1.0 - (cy / _point->line().height());
3604 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3606 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3610 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3612 if (!movement_occurred) {
3616 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3617 _editor->reset_point_selection ();
3621 motion (event, false);
3624 _point->line().end_drag (_pushing, _final_index);
3625 _editor->session()->commit_reversible_command ();
3629 ControlPointDrag::aborted (bool)
3631 _point->line().reset ();
3635 ControlPointDrag::active (Editing::MouseMode m)
3637 if (m == Editing::MouseGain) {
3638 /* always active in mouse gain */
3642 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3643 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3646 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3649 _cumulative_y_drag (0)
3651 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3655 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3657 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3660 _item = &_line->grab_item ();
3662 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3663 origin, and ditto for y.
3666 double cx = event->button.x;
3667 double cy = event->button.y;
3669 _line->parent_group().canvas_to_item (cx, cy);
3671 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3676 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3677 /* no adjacent points */
3681 Drag::start_grab (event, _editor->cursors()->fader);
3683 /* store grab start in parent frame */
3688 double fraction = 1.0 - (cy / _line->height());
3690 _line->start_drag_line (before, after, fraction);
3692 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3696 LineDrag::motion (GdkEvent* event, bool)
3698 double dy = current_pointer_y() - last_pointer_y();
3700 if (event->button.state & Keyboard::SecondaryModifier) {
3704 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3706 _cumulative_y_drag = cy - _fixed_grab_y;
3709 cy = min ((double) _line->height(), cy);
3711 double const fraction = 1.0 - (cy / _line->height());
3714 /* we are ignoring x position for this drag, so we can just pass in anything */
3715 _line->drag_motion (0, fraction, true, false, ignored);
3717 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3721 LineDrag::finished (GdkEvent* event, bool movement_occured)
3723 if (movement_occured) {
3724 motion (event, false);
3725 _line->end_drag (false, 0);
3727 /* add a new control point on the line */
3729 AutomationTimeAxisView* atv;
3731 _line->end_drag (false, 0);
3733 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3734 framepos_t where = _editor->window_event_sample (event, 0, 0);
3735 atv->add_automation_event (event, where, event->button.y, false);
3739 _editor->session()->commit_reversible_command ();
3743 LineDrag::aborted (bool)
3748 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3751 _cumulative_x_drag (0)
3753 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3757 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3759 Drag::start_grab (event);
3761 _line = reinterpret_cast<Line*> (_item);
3764 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3766 double cx = event->button.x;
3767 double cy = event->button.y;
3769 _item->parent()->canvas_to_item (cx, cy);
3771 /* store grab start in parent frame */
3772 _region_view_grab_x = cx;
3774 _before = *(float*) _item->get_data ("position");
3776 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3778 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3782 FeatureLineDrag::motion (GdkEvent*, bool)
3784 double dx = _drags->current_pointer_x() - last_pointer_x();
3786 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3788 _cumulative_x_drag += dx;
3790 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3799 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3801 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3803 float *pos = new float;
3806 _line->set_data ("position", pos);
3812 FeatureLineDrag::finished (GdkEvent*, bool)
3814 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3815 _arv->update_transient(_before, _before);
3819 FeatureLineDrag::aborted (bool)
3824 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3826 , _vertical_only (false)
3828 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3832 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3834 Drag::start_grab (event);
3835 show_verbose_cursor_time (adjusted_current_frame (event));
3839 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3846 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3848 framepos_t grab = grab_frame ();
3849 if (Config->get_rubberbanding_snaps_to_grid ()) {
3850 _editor->snap_to_with_modifier (grab, event);
3853 /* base start and end on initial click position */
3863 if (current_pointer_y() < grab_y()) {
3864 y1 = current_pointer_y();
3867 y2 = current_pointer_y();
3871 if (start != end || y1 != y2) {
3873 double x1 = _editor->sample_to_pixel (start);
3874 double x2 = _editor->sample_to_pixel (end);
3875 const double min_dimension = 2.0;
3877 if (_vertical_only) {
3878 /* fixed 10 pixel width */
3882 x2 = min (x1 - min_dimension, x2);
3884 x2 = max (x1 + min_dimension, x2);
3889 y2 = min (y1 - min_dimension, y2);
3891 y2 = max (y1 + min_dimension, y2);
3894 /* translate rect into item space and set */
3896 ArdourCanvas::Rect r (x1, y1, x2, y2);
3898 /* this drag is a _trackview_only == true drag, so the y1 and
3899 * y2 (computed using current_pointer_y() and grab_y()) will be
3900 * relative to the top of the trackview group). The
3901 * rubberband rect has the same parent/scroll offset as the
3902 * the trackview group, so we can use the "r" rect directly
3903 * to set the shape of the rubberband.
3906 _editor->rubberband_rect->set (r);
3907 _editor->rubberband_rect->show();
3908 _editor->rubberband_rect->raise_to_top();
3910 show_verbose_cursor_time (pf);
3912 do_select_things (event, true);
3917 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3922 if (grab_frame() < last_pointer_frame()) {
3924 x2 = last_pointer_frame ();
3927 x1 = last_pointer_frame ();
3933 if (current_pointer_y() < grab_y()) {
3934 y1 = current_pointer_y();
3937 y2 = current_pointer_y();
3941 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3945 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3947 if (movement_occurred) {
3949 motion (event, false);
3950 do_select_things (event, false);
3956 bool do_deselect = true;
3957 MidiTimeAxisView* mtv;
3959 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3961 if (_editor->selection->empty()) {
3962 /* nothing selected */
3963 add_midi_region (mtv);
3964 do_deselect = false;
3968 /* do not deselect if Primary or Tertiary (toggle-select or
3969 * extend-select are pressed.
3972 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3973 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3980 _editor->rubberband_rect->hide();
3984 RubberbandSelectDrag::aborted (bool)
3986 _editor->rubberband_rect->hide ();
3989 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3990 : RegionDrag (e, i, p, v)
3992 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3996 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3998 Drag::start_grab (event, cursor);
4000 show_verbose_cursor_time (adjusted_current_frame (event));
4004 TimeFXDrag::motion (GdkEvent* event, bool)
4006 RegionView* rv = _primary;
4007 StreamView* cv = rv->get_time_axis_view().view ();
4009 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4010 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4011 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4013 framepos_t const pf = adjusted_current_frame (event);
4015 if (pf > rv->region()->position()) {
4016 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4019 show_verbose_cursor_time (pf);
4023 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4025 _primary->get_time_axis_view().hide_timestretch ();
4027 if (!movement_occurred) {
4031 if (last_pointer_frame() < _primary->region()->position()) {
4032 /* backwards drag of the left edge - not usable */
4036 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4038 float percentage = (double) newlen / (double) _primary->region()->length();
4040 #ifndef USE_RUBBERBAND
4041 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4042 if (_primary->region()->data_type() == DataType::AUDIO) {
4043 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4047 if (!_editor->get_selection().regions.empty()) {
4048 /* primary will already be included in the selection, and edit
4049 group shared editing will propagate selection across
4050 equivalent regions, so just use the current region
4054 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4055 error << _("An error occurred while executing time stretch operation") << endmsg;
4061 TimeFXDrag::aborted (bool)
4063 _primary->get_time_axis_view().hide_timestretch ();
4066 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4069 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4073 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4075 Drag::start_grab (event);
4079 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4081 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4085 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4087 if (movement_occurred && _editor->session()) {
4088 /* make sure we stop */
4089 _editor->session()->request_transport_speed (0.0);
4094 ScrubDrag::aborted (bool)
4099 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4104 , _original_pointer_time_axis (-1)
4105 , _last_pointer_time_axis (-1)
4106 , _time_selection_at_start (!_editor->get_selection().time.empty())
4108 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4110 if (_time_selection_at_start) {
4111 start_at_start = _editor->get_selection().time.start();
4112 end_at_start = _editor->get_selection().time.end_frame();
4117 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4119 if (_editor->session() == 0) {
4123 Gdk::Cursor* cursor = 0;
4125 switch (_operation) {
4126 case CreateSelection:
4127 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4132 cursor = _editor->cursors()->selector;
4133 Drag::start_grab (event, cursor);
4136 case SelectionStartTrim:
4137 if (_editor->clicked_axisview) {
4138 _editor->clicked_axisview->order_selection_trims (_item, true);
4140 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4143 case SelectionEndTrim:
4144 if (_editor->clicked_axisview) {
4145 _editor->clicked_axisview->order_selection_trims (_item, false);
4147 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4151 Drag::start_grab (event, cursor);
4154 case SelectionExtend:
4155 Drag::start_grab (event, cursor);
4159 if (_operation == SelectionMove) {
4160 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4162 show_verbose_cursor_time (adjusted_current_frame (event));
4165 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4169 SelectionDrag::setup_pointer_frame_offset ()
4171 switch (_operation) {
4172 case CreateSelection:
4173 _pointer_frame_offset = 0;
4176 case SelectionStartTrim:
4178 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4181 case SelectionEndTrim:
4182 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4185 case SelectionExtend:
4191 SelectionDrag::motion (GdkEvent* event, bool first_move)
4193 framepos_t start = 0;
4195 framecnt_t length = 0;
4196 framecnt_t distance = 0;
4198 framepos_t const pending_position = adjusted_current_frame (event);
4200 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4204 switch (_operation) {
4205 case CreateSelection:
4207 framepos_t grab = grab_frame ();
4210 grab = adjusted_current_frame (event, false);
4211 if (grab < pending_position) {
4212 _editor->snap_to (grab, -1);
4214 _editor->snap_to (grab, 1);
4218 if (pending_position < grab) {
4219 start = pending_position;
4222 end = pending_position;
4226 /* first drag: Either add to the selection
4227 or create a new selection
4234 /* adding to the selection */
4235 _editor->set_selected_track_as_side_effect (Selection::Add);
4236 _editor->clicked_selection = _editor->selection->add (start, end);
4243 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4244 _editor->set_selected_track_as_side_effect (Selection::Set);
4247 _editor->clicked_selection = _editor->selection->set (start, end);
4251 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4252 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4253 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4255 _editor->selection->add (atest);
4259 /* select all tracks within the rectangle that we've marked out so far */
4260 TrackViewList new_selection;
4261 TrackViewList& all_tracks (_editor->track_views);
4263 ArdourCanvas::Coord const top = grab_y();
4264 ArdourCanvas::Coord const bottom = current_pointer_y();
4266 if (top >= 0 && bottom >= 0) {
4268 //first, find the tracks that are covered in the y range selection
4269 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4270 if ((*i)->covered_by_y_range (top, bottom)) {
4271 new_selection.push_back (*i);
4275 //now find any tracks that are GROUPED with the tracks we selected
4276 TrackViewList grouped_add = new_selection;
4277 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4278 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4279 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4280 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4281 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4282 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4283 grouped_add.push_back (*j);
4288 //now compare our list with the current selection, and add or remove as necessary
4289 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4290 TrackViewList tracks_to_add;
4291 TrackViewList tracks_to_remove;
4292 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4293 if ( !_editor->selection->tracks.contains ( *i ) )
4294 tracks_to_add.push_back ( *i );
4295 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4296 if ( !grouped_add.contains ( *i ) )
4297 tracks_to_remove.push_back ( *i );
4298 _editor->selection->add(tracks_to_add);
4299 _editor->selection->remove(tracks_to_remove);
4305 case SelectionStartTrim:
4307 start = _editor->selection->time[_editor->clicked_selection].start;
4308 end = _editor->selection->time[_editor->clicked_selection].end;
4310 if (pending_position > end) {
4313 start = pending_position;
4317 case SelectionEndTrim:
4319 start = _editor->selection->time[_editor->clicked_selection].start;
4320 end = _editor->selection->time[_editor->clicked_selection].end;
4322 if (pending_position < start) {
4325 end = pending_position;
4332 start = _editor->selection->time[_editor->clicked_selection].start;
4333 end = _editor->selection->time[_editor->clicked_selection].end;
4335 length = end - start;
4336 distance = pending_position - start;
4337 start = pending_position;
4338 _editor->snap_to (start);
4340 end = start + length;
4344 case SelectionExtend:
4349 switch (_operation) {
4351 if (_time_selection_at_start) {
4352 _editor->selection->move_time (distance);
4356 _editor->selection->replace (_editor->clicked_selection, start, end);
4360 if (_operation == SelectionMove) {
4361 show_verbose_cursor_time(start);
4363 show_verbose_cursor_time(pending_position);
4368 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4370 Session* s = _editor->session();
4372 if (movement_occurred) {
4373 motion (event, false);
4374 /* XXX this is not object-oriented programming at all. ick */
4375 if (_editor->selection->time.consolidate()) {
4376 _editor->selection->TimeChanged ();
4379 /* XXX what if its a music time selection? */
4381 if ( s->get_play_range() && s->transport_rolling() ) {
4382 s->request_play_range (&_editor->selection->time, true);
4384 if (Config->get_follow_edits() && !s->transport_rolling()) {
4385 if (_operation == SelectionEndTrim)
4386 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4388 s->request_locate (_editor->get_selection().time.start());
4394 /* just a click, no pointer movement.
4397 if (_operation == SelectionExtend) {
4398 if (_time_selection_at_start) {
4399 framepos_t pos = adjusted_current_frame (event, false);
4400 framepos_t start = min (pos, start_at_start);
4401 framepos_t end = max (pos, end_at_start);
4402 _editor->selection->set (start, end);
4405 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4406 if (_editor->clicked_selection) {
4407 _editor->selection->remove (_editor->clicked_selection);
4410 if (!_editor->clicked_selection) {
4411 _editor->selection->clear_time();
4416 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4417 _editor->selection->set (_editor->clicked_axisview);
4420 if (s && s->get_play_range () && s->transport_rolling()) {
4421 s->request_stop (false, false);
4426 _editor->stop_canvas_autoscroll ();
4427 _editor->clicked_selection = 0;
4431 SelectionDrag::aborted (bool)
4436 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4437 : Drag (e, i, false),
4441 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4443 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4444 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4445 physical_screen_height (_editor->get_window())));
4446 _drag_rect->hide ();
4448 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_RangeDragRect());
4449 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_RangeDragRect());
4453 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4455 if (_editor->session() == 0) {
4459 Gdk::Cursor* cursor = 0;
4461 if (!_editor->temp_location) {
4462 _editor->temp_location = new Location (*_editor->session());
4465 switch (_operation) {
4466 case CreateSkipMarker:
4467 case CreateRangeMarker:
4468 case CreateTransportMarker:
4469 case CreateCDMarker:
4471 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4476 cursor = _editor->cursors()->selector;
4480 Drag::start_grab (event, cursor);
4482 show_verbose_cursor_time (adjusted_current_frame (event));
4486 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4488 framepos_t start = 0;
4490 ArdourCanvas::Rectangle *crect;
4492 switch (_operation) {
4493 case CreateSkipMarker:
4494 crect = _editor->range_bar_drag_rect;
4496 case CreateRangeMarker:
4497 crect = _editor->range_bar_drag_rect;
4499 case CreateTransportMarker:
4500 crect = _editor->transport_bar_drag_rect;
4502 case CreateCDMarker:
4503 crect = _editor->cd_marker_bar_drag_rect;
4506 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4511 framepos_t const pf = adjusted_current_frame (event);
4513 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4514 framepos_t grab = grab_frame ();
4515 _editor->snap_to (grab);
4517 if (pf < grab_frame()) {
4525 /* first drag: Either add to the selection
4526 or create a new selection.
4531 _editor->temp_location->set (start, end);
4535 update_item (_editor->temp_location);
4537 //_drag_rect->raise_to_top();
4543 _editor->temp_location->set (start, end);
4545 double x1 = _editor->sample_to_pixel (start);
4546 double x2 = _editor->sample_to_pixel (end);
4550 update_item (_editor->temp_location);
4553 show_verbose_cursor_time (pf);
4558 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4560 Location * newloc = 0;
4564 if (movement_occurred) {
4565 motion (event, false);
4568 switch (_operation) {
4569 case CreateSkipMarker:
4570 case CreateRangeMarker:
4571 case CreateCDMarker:
4573 XMLNode &before = _editor->session()->locations()->get_state();
4574 if (_operation == CreateSkipMarker) {
4575 _editor->begin_reversible_command (_("new skip marker"));
4576 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4577 flags = Location::IsRangeMarker | Location::IsSkip;
4578 _editor->range_bar_drag_rect->hide();
4579 } else if (_operation == CreateCDMarker) {
4580 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4581 _editor->begin_reversible_command (_("new CD marker"));
4582 flags = Location::IsRangeMarker | Location::IsCDMarker;
4583 _editor->cd_marker_bar_drag_rect->hide();
4585 _editor->begin_reversible_command (_("new skip marker"));
4586 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4587 flags = Location::IsRangeMarker;
4588 _editor->range_bar_drag_rect->hide();
4590 newloc = new Location (
4591 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4594 _editor->session()->locations()->add (newloc, true);
4595 XMLNode &after = _editor->session()->locations()->get_state();
4596 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4597 _editor->commit_reversible_command ();
4601 case CreateTransportMarker:
4602 // popup menu to pick loop or punch
4603 _editor->new_transport_marker_context_menu (&event->button, _item);
4609 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4611 if (_operation == CreateTransportMarker) {
4613 /* didn't drag, so just locate */
4615 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4617 } else if (_operation == CreateCDMarker) {
4619 /* didn't drag, but mark is already created so do
4622 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
4628 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4630 if (end == max_framepos) {
4631 end = _editor->session()->current_end_frame ();
4634 if (start == max_framepos) {
4635 start = _editor->session()->current_start_frame ();
4638 switch (_editor->mouse_mode) {
4640 /* find the two markers on either side and then make the selection from it */
4641 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4645 /* find the two markers on either side of the click and make the range out of it */
4646 _editor->selection->set (start, end);
4655 _editor->stop_canvas_autoscroll ();
4659 RangeMarkerBarDrag::aborted (bool)
4665 RangeMarkerBarDrag::update_item (Location* location)
4667 double const x1 = _editor->sample_to_pixel (location->start());
4668 double const x2 = _editor->sample_to_pixel (location->end());
4670 _drag_rect->set_x0 (x1);
4671 _drag_rect->set_x1 (x2);
4674 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4676 , _cumulative_dx (0)
4677 , _cumulative_dy (0)
4679 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4681 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4683 _region = &_primary->region_view ();
4684 _note_height = _region->midi_stream_view()->note_height ();
4688 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4690 Drag::start_grab (event);
4692 if (!(_was_selected = _primary->selected())) {
4694 /* tertiary-click means extend selection - we'll do that on button release,
4695 so don't add it here, because otherwise we make it hard to figure
4696 out the "extend-to" range.
4699 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4702 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4705 _region->note_selected (_primary, true);
4707 _region->unique_select (_primary);
4713 /** @return Current total drag x change in frames */
4715 NoteDrag::total_dx () const
4718 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4720 /* primary note time */
4721 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4723 /* new time of the primary note in session frames */
4724 frameoffset_t st = n + dx;
4726 framepos_t const rp = _region->region()->position ();
4728 /* prevent the note being dragged earlier than the region's position */
4731 /* snap and return corresponding delta */
4732 return _region->snap_frame_to_frame (st - rp) + rp - n;
4735 /** @return Current total drag y change in note number */
4737 NoteDrag::total_dy () const
4739 MidiStreamView* msv = _region->midi_stream_view ();
4740 double const y = _region->midi_view()->y_position ();
4741 /* new current note */
4742 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4744 n = max (msv->lowest_note(), n);
4745 n = min (msv->highest_note(), n);
4746 /* and work out delta */
4747 return n - msv->y_to_note (grab_y() - y);
4751 NoteDrag::motion (GdkEvent *, bool)
4753 /* Total change in x and y since the start of the drag */
4754 frameoffset_t const dx = total_dx ();
4755 int8_t const dy = total_dy ();
4757 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4758 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4759 double const tdy = -dy * _note_height - _cumulative_dy;
4762 _cumulative_dx += tdx;
4763 _cumulative_dy += tdy;
4765 int8_t note_delta = total_dy();
4767 _region->move_selection (tdx, tdy, note_delta);
4769 /* the new note value may be the same as the old one, but we
4770 * don't know what that means because the selection may have
4771 * involved more than one note and we might be doing something
4772 * odd with them. so show the note value anyway, always.
4776 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4778 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4779 (int) floor ((double)new_note));
4781 show_verbose_cursor_text (buf);
4786 NoteDrag::finished (GdkEvent* ev, bool moved)
4789 /* no motion - select note */
4791 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4792 _editor->current_mouse_mode() == Editing::MouseDraw) {
4794 if (_was_selected) {
4795 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4797 _region->note_deselected (_primary);
4800 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4801 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4803 if (!extend && !add && _region->selection_size() > 1) {
4804 _region->unique_select (_primary);
4805 } else if (extend) {
4806 _region->note_selected (_primary, true, true);
4808 /* it was added during button press */
4813 _region->note_dropped (_primary, total_dx(), total_dy());
4818 NoteDrag::aborted (bool)
4823 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4824 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4825 : Drag (editor, atv->base_item ())
4827 , _nothing_to_drag (false)
4829 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4830 y_origin = atv->y_position();
4831 setup (atv->lines ());
4834 /** Make an AutomationRangeDrag for region gain lines */
4835 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4836 : Drag (editor, rv->get_canvas_group ())
4838 , _nothing_to_drag (false)
4840 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4842 list<boost::shared_ptr<AutomationLine> > lines;
4843 lines.push_back (rv->get_gain_line ());
4844 y_origin = rv->get_time_axis_view().y_position();
4848 /** @param lines AutomationLines to drag.
4849 * @param offset Offset from the session start to the points in the AutomationLines.
4852 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4854 /* find the lines that overlap the ranges being dragged */
4855 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4856 while (i != lines.end ()) {
4857 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4860 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4862 /* check this range against all the AudioRanges that we are using */
4863 list<AudioRange>::const_iterator k = _ranges.begin ();
4864 while (k != _ranges.end()) {
4865 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4871 /* add it to our list if it overlaps at all */
4872 if (k != _ranges.end()) {
4877 _lines.push_back (n);
4883 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4887 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4889 return 1.0 - ((global_y - y_origin) / line->height());
4893 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4895 Drag::start_grab (event, cursor);
4897 /* Get line states before we start changing things */
4898 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4899 i->state = &i->line->get_state ();
4900 i->original_fraction = y_fraction (i->line, current_pointer_y());
4903 if (_ranges.empty()) {
4905 /* No selected time ranges: drag all points */
4906 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4907 uint32_t const N = i->line->npoints ();
4908 for (uint32_t j = 0; j < N; ++j) {
4909 i->points.push_back (i->line->nth (j));
4915 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4917 framecnt_t const half = (i->start + i->end) / 2;
4919 /* find the line that this audio range starts in */
4920 list<Line>::iterator j = _lines.begin();
4921 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4925 if (j != _lines.end()) {
4926 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4928 /* j is the line that this audio range starts in; fade into it;
4929 64 samples length plucked out of thin air.
4932 framepos_t a = i->start + 64;
4937 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4938 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4940 the_list->editor_add (p, the_list->eval (p));
4941 the_list->editor_add (q, the_list->eval (q));
4944 /* same thing for the end */
4947 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4951 if (j != _lines.end()) {
4952 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4954 /* j is the line that this audio range starts in; fade out of it;
4955 64 samples length plucked out of thin air.
4958 framepos_t b = i->end - 64;
4963 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4964 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4966 the_list->editor_add (p, the_list->eval (p));
4967 the_list->editor_add (q, the_list->eval (q));
4971 _nothing_to_drag = true;
4973 /* Find all the points that should be dragged and put them in the relevant
4974 points lists in the Line structs.
4977 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4979 uint32_t const N = i->line->npoints ();
4980 for (uint32_t j = 0; j < N; ++j) {
4982 /* here's a control point on this line */
4983 ControlPoint* p = i->line->nth (j);
4984 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4986 /* see if it's inside a range */
4987 list<AudioRange>::const_iterator k = _ranges.begin ();
4988 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4992 if (k != _ranges.end()) {
4993 /* dragging this point */
4994 _nothing_to_drag = false;
4995 i->points.push_back (p);
5001 if (_nothing_to_drag) {
5005 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5006 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5011 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5013 if (_nothing_to_drag) {
5017 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5018 float const f = y_fraction (l->line, current_pointer_y());
5019 /* we are ignoring x position for this drag, so we can just pass in anything */
5021 l->line->drag_motion (0, f, true, false, ignored);
5022 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5027 AutomationRangeDrag::finished (GdkEvent* event, bool)
5029 if (_nothing_to_drag) {
5033 motion (event, false);
5034 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5035 i->line->end_drag (false, 0);
5038 _editor->session()->commit_reversible_command ();
5042 AutomationRangeDrag::aborted (bool)
5044 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5049 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5051 , initial_time_axis_view (itav)
5053 /* note that time_axis_view may be null if the regionview was created
5054 * as part of a copy operation.
5056 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5057 layer = v->region()->layer ();
5058 initial_y = v->get_canvas_group()->position().y;
5059 initial_playlist = v->region()->playlist ();
5060 initial_position = v->region()->position ();
5061 initial_end = v->region()->position () + v->region()->length ();
5064 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5065 : Drag (e, i->canvas_item ())
5068 , _cumulative_dx (0)
5070 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5071 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5076 PatchChangeDrag::motion (GdkEvent* ev, bool)
5078 framepos_t f = adjusted_current_frame (ev);
5079 boost::shared_ptr<Region> r = _region_view->region ();
5080 f = max (f, r->position ());
5081 f = min (f, r->last_frame ());
5083 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5084 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5085 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5086 _cumulative_dx = dxu;
5090 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5092 if (!movement_occurred) {
5096 boost::shared_ptr<Region> r (_region_view->region ());
5097 framepos_t f = adjusted_current_frame (ev);
5098 f = max (f, r->position ());
5099 f = min (f, r->last_frame ());
5101 _region_view->move_patch_change (
5103 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5108 PatchChangeDrag::aborted (bool)
5110 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5114 PatchChangeDrag::setup_pointer_frame_offset ()
5116 boost::shared_ptr<Region> region = _region_view->region ();
5117 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5120 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5121 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5128 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5130 framepos_t const p = _region_view->region()->position ();
5131 double const y = _region_view->midi_view()->y_position ();
5133 x1 = max ((framepos_t) 0, x1 - p);
5134 x2 = max ((framepos_t) 0, x2 - p);
5135 y1 = max (0.0, y1 - y);
5136 y2 = max (0.0, y2 - y);
5138 _region_view->update_drag_selection (
5139 _editor->sample_to_pixel (x1),
5140 _editor->sample_to_pixel (x2),
5143 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5148 MidiRubberbandSelectDrag::deselect_things ()
5153 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5154 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5157 _vertical_only = true;
5161 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5163 double const y = _region_view->midi_view()->y_position ();
5165 y1 = max (0.0, y1 - y);
5166 y2 = max (0.0, y2 - y);
5168 _region_view->update_vertical_drag_selection (
5171 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5176 MidiVerticalSelectDrag::deselect_things ()
5181 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5182 : RubberbandSelectDrag (e, i)
5188 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5190 if (drag_in_progress) {
5191 /* We just want to select things at the end of the drag, not during it */
5195 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5197 _editor->begin_reversible_command (_("rubberband selection"));
5198 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5199 _editor->commit_reversible_command ();
5203 EditorRubberbandSelectDrag::deselect_things ()
5205 if (!getenv("ARDOUR_SAE")) {
5206 _editor->selection->clear_tracks();
5208 _editor->selection->clear_regions();
5209 _editor->selection->clear_points ();
5210 _editor->selection->clear_lines ();
5213 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5221 NoteCreateDrag::~NoteCreateDrag ()
5227 NoteCreateDrag::grid_frames (framepos_t t) const
5230 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
5235 return _region_view->region_beats_to_region_frames (grid_beats);
5239 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5241 Drag::start_grab (event, cursor);
5243 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5245 framepos_t pf = _drags->current_pointer_frame ();
5246 framecnt_t const g = grid_frames (pf);
5248 /* Hack so that we always snap to the note that we are over, instead of snapping
5249 to the next one if we're more than halfway through the one we're over.
5251 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5255 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5257 MidiStreamView* sv = _region_view->midi_stream_view ();
5258 double const x = _editor->sample_to_pixel (_note[0]);
5259 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5261 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5262 _drag_rect->set_outline_all ();
5263 _drag_rect->set_outline_color (0xffffff99);
5264 _drag_rect->set_fill_color (0xffffff66);
5268 NoteCreateDrag::motion (GdkEvent* event, bool)
5270 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5271 double const x = _editor->sample_to_pixel (_note[1]);
5272 if (_note[1] > _note[0]) {
5273 _drag_rect->set_x1 (x);
5275 _drag_rect->set_x0 (x);
5280 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5282 if (!had_movement) {
5286 framepos_t const start = min (_note[0], _note[1]);
5287 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5289 framecnt_t const g = grid_frames (start);
5290 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5292 if (_editor->snap_mode() == SnapNormal && length < g) {
5293 length = g - one_tick;
5296 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5298 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5302 NoteCreateDrag::y_to_region (double y) const
5305 _region_view->get_canvas_group()->canvas_to_item (x, y);
5310 NoteCreateDrag::aborted (bool)
5315 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5320 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5324 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5326 Drag::start_grab (event, cursor);
5330 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5336 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5339 distance = _drags->current_pointer_x() - grab_x();
5340 len = ar->fade_in()->back()->when;
5342 distance = grab_x() - _drags->current_pointer_x();
5343 len = ar->fade_out()->back()->when;
5346 /* how long should it be ? */
5348 new_length = len + _editor->pixel_to_sample (distance);
5350 /* now check with the region that this is legal */
5352 new_length = ar->verify_xfade_bounds (new_length, start);
5355 arv->reset_fade_in_shape_width (ar, new_length);
5357 arv->reset_fade_out_shape_width (ar, new_length);
5362 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5368 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5371 distance = _drags->current_pointer_x() - grab_x();
5372 len = ar->fade_in()->back()->when;
5374 distance = grab_x() - _drags->current_pointer_x();
5375 len = ar->fade_out()->back()->when;
5378 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5380 _editor->begin_reversible_command ("xfade trim");
5381 ar->playlist()->clear_owned_changes ();
5384 ar->set_fade_in_length (new_length);
5386 ar->set_fade_out_length (new_length);
5389 /* Adjusting the xfade may affect other regions in the playlist, so we need
5390 to get undo Commands from the whole playlist rather than just the
5394 vector<Command*> cmds;
5395 ar->playlist()->rdiff (cmds);
5396 _editor->session()->add_commands (cmds);
5397 _editor->commit_reversible_command ();
5402 CrossfadeEdgeDrag::aborted (bool)
5405 arv->redraw_start_xfade ();
5407 arv->redraw_end_xfade ();
5411 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5412 : Drag (e, item, true)
5413 , line (new EditorCursor (*e))
5415 line->set_position (pos);
5419 RegionCutDrag::~RegionCutDrag ()
5425 RegionCutDrag::motion (GdkEvent*, bool)
5427 framepos_t where = _drags->current_pointer_frame();
5428 _editor->snap_to (where);
5430 line->set_position (where);
5434 RegionCutDrag::finished (GdkEvent*, bool)
5436 _editor->get_track_canvas()->canvas()->re_enter();
5438 framepos_t pos = _drags->current_pointer_frame();
5442 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5448 _editor->split_regions_at (pos, rs);
5452 RegionCutDrag::aborted (bool)