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/scroll_group.h"
48 #include "audio_region_view.h"
49 #include "midi_region_view.h"
50 #include "ardour_ui.h"
51 #include "gui_thread.h"
52 #include "control_point.h"
53 #include "region_gain_line.h"
54 #include "editor_drag.h"
55 #include "audio_time_axis.h"
56 #include "midi_time_axis.h"
57 #include "selection.h"
58 #include "midi_selection.h"
59 #include "automation_time_axis.h"
61 #include "editor_cursors.h"
62 #include "mouse_cursors.h"
63 #include "note_base.h"
64 #include "patch_change.h"
65 #include "verbose_cursor.h"
68 using namespace ARDOUR;
71 using namespace Gtkmm2ext;
72 using namespace Editing;
73 using namespace ArdourCanvas;
75 using Gtkmm2ext::Keyboard;
77 double ControlPointDrag::_zero_gain_fraction = -1.0;
79 DragManager::DragManager (Editor* e)
82 , _current_pointer_frame (0)
86 DragManager::~DragManager ()
91 /** Call abort for each active drag */
97 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
102 if (!_drags.empty ()) {
103 _editor->set_follow_playhead (_old_follow_playhead, false);
112 DragManager::add (Drag* d)
114 d->set_manager (this);
115 _drags.push_back (d);
119 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
121 d->set_manager (this);
122 _drags.push_back (d);
127 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
129 /* Prevent follow playhead during the drag to be nice to the user */
130 _old_follow_playhead = _editor->follow_playhead ();
131 _editor->set_follow_playhead (false);
133 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
135 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
136 (*i)->start_grab (e, c);
140 /** Call end_grab for each active drag.
141 * @return true if any drag reported movement having occurred.
144 DragManager::end_grab (GdkEvent* e)
149 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
150 bool const t = (*i)->end_grab (e);
161 _editor->set_follow_playhead (_old_follow_playhead, false);
167 DragManager::mark_double_click ()
169 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
170 (*i)->set_double_click (true);
175 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
179 /* calling this implies that we expect the event to have canvas
182 * Can we guarantee that this is true?
185 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
187 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
188 bool const t = (*i)->motion_handler (e, from_autoscroll);
189 /* run all handlers; return true if at least one of them
190 returns true (indicating that the event has been handled).
202 DragManager::have_item (ArdourCanvas::Item* i) const
204 list<Drag*>::const_iterator j = _drags.begin ();
205 while (j != _drags.end() && (*j)->item () != i) {
209 return j != _drags.end ();
212 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
215 , _pointer_frame_offset (0)
216 , _trackview_only (trackview_only)
217 , _move_threshold_passed (false)
218 , _was_double_click (false)
219 , _raw_grab_frame (0)
221 , _last_pointer_frame (0)
227 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
240 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
242 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
244 if (Keyboard::is_button2_event (&event->button)) {
245 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
246 _y_constrained = true;
247 _x_constrained = false;
249 _y_constrained = false;
250 _x_constrained = true;
253 _x_constrained = false;
254 _y_constrained = false;
257 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
258 setup_pointer_frame_offset ();
259 _grab_frame = adjusted_frame (_raw_grab_frame, event);
260 _last_pointer_frame = _grab_frame;
261 _last_pointer_x = _grab_x;
263 if (_trackview_only) {
264 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
267 _last_pointer_y = _grab_y;
272 /* CAIROCANVAS need a variant here that passes *cursor */
274 _editor->push_canvas_cursor (cursor);
277 if (_editor->session() && _editor->session()->transport_rolling()) {
280 _was_rolling = false;
283 switch (_editor->snap_type()) {
284 case SnapToRegionStart:
285 case SnapToRegionEnd:
286 case SnapToRegionSync:
287 case SnapToRegionBoundary:
288 _editor->build_region_boundary_cache ();
295 /** Call to end a drag `successfully'. Ungrabs item and calls
296 * subclass' finished() method.
298 * @param event GDK event, or 0.
299 * @return true if some movement occurred, otherwise false.
302 Drag::end_grab (GdkEvent* event)
304 _editor->stop_canvas_autoscroll ();
308 finished (event, _move_threshold_passed);
310 _editor->verbose_cursor()->hide ();
311 _editor->pop_canvas_cursor ();
313 return _move_threshold_passed;
317 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
321 if (f > _pointer_frame_offset) {
322 pos = f - _pointer_frame_offset;
326 _editor->snap_to_with_modifier (pos, event);
333 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
335 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
339 Drag::current_pointer_y () const
341 if (!_trackview_only) {
342 return _drags->current_pointer_y ();
345 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
349 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
351 /* check to see if we have moved in any way that matters since the last motion event */
352 if (_move_threshold_passed &&
353 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
354 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
358 pair<framecnt_t, int> const threshold = move_threshold ();
360 bool const old_move_threshold_passed = _move_threshold_passed;
362 if (!from_autoscroll && !_move_threshold_passed) {
364 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
365 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
367 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
370 if (active (_editor->mouse_mode) && _move_threshold_passed) {
372 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
373 if (!from_autoscroll) {
374 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
377 if (!_editor->autoscroll_active() || from_autoscroll) {
378 motion (event, _move_threshold_passed != old_move_threshold_passed);
380 _last_pointer_x = _drags->current_pointer_x ();
381 _last_pointer_y = current_pointer_y ();
382 _last_pointer_frame = adjusted_current_frame (event);
392 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
400 aborted (_move_threshold_passed);
402 _editor->stop_canvas_autoscroll ();
403 _editor->verbose_cursor()->hide ();
407 Drag::show_verbose_cursor_time (framepos_t frame)
409 _editor->verbose_cursor()->set_time (frame);
410 _editor->verbose_cursor()->show ();
414 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
416 _editor->verbose_cursor()->set_duration (start, end);
417 _editor->verbose_cursor()->show ();
421 Drag::show_verbose_cursor_text (string const & text)
423 _editor->verbose_cursor()->set (text);
424 _editor->verbose_cursor()->show ();
427 boost::shared_ptr<Region>
428 Drag::add_midi_region (MidiTimeAxisView* view)
430 if (_editor->session()) {
431 const TempoMap& map (_editor->session()->tempo_map());
432 framecnt_t pos = grab_frame();
433 const Meter& m = map.meter_at (pos);
434 /* not that the frame rate used here can be affected by pull up/down which
437 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
438 return view->add_region (grab_frame(), len, true);
441 return boost::shared_ptr<Region>();
444 struct EditorOrderTimeAxisViewSorter {
445 bool operator() (TimeAxisView* a, TimeAxisView* b) {
446 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
447 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
449 return ra->route()->order_key () < rb->route()->order_key ();
453 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
457 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
459 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
460 as some of the regions we are dragging may be on such tracks.
463 TrackViewList track_views = _editor->track_views;
464 track_views.sort (EditorOrderTimeAxisViewSorter ());
466 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
467 _time_axis_views.push_back (*i);
469 TimeAxisView::Children children_list = (*i)->get_child_list ();
470 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
471 _time_axis_views.push_back (j->get());
475 /* the list of views can be empty at this point if this is a region list-insert drag
478 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
479 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
482 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
486 RegionDrag::region_going_away (RegionView* v)
488 list<DraggingView>::iterator i = _views.begin ();
489 while (i != _views.end() && i->view != v) {
493 if (i != _views.end()) {
498 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
499 * or -1 if it is not found.
502 RegionDrag::find_time_axis_view (TimeAxisView* t) const
505 int const N = _time_axis_views.size ();
506 while (i < N && _time_axis_views[i] != t) {
517 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
518 : RegionDrag (e, i, p, v)
521 , _last_pointer_time_axis_view (0)
522 , _last_pointer_layer (0)
524 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
528 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
530 Drag::start_grab (event, cursor);
532 show_verbose_cursor_time (_last_frame_position);
534 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
536 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
537 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
542 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
544 /* compute the amount of pointer motion in frames, and where
545 the region would be if we moved it by that much.
547 *pending_region_position = adjusted_current_frame (event);
549 framepos_t sync_frame;
550 framecnt_t sync_offset;
553 sync_offset = _primary->region()->sync_offset (sync_dir);
555 /* we don't handle a sync point that lies before zero.
557 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
559 sync_frame = *pending_region_position + (sync_dir*sync_offset);
561 _editor->snap_to_with_modifier (sync_frame, event);
563 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
566 *pending_region_position = _last_frame_position;
569 if (*pending_region_position > max_framepos - _primary->region()->length()) {
570 *pending_region_position = _last_frame_position;
575 /* in locked edit mode, reverse the usual meaning of _x_constrained */
576 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
578 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
580 /* x movement since last time (in pixels) */
581 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
583 /* total x movement */
584 framecnt_t total_dx = *pending_region_position;
585 if (regions_came_from_canvas()) {
586 total_dx = total_dx - grab_frame ();
589 /* check that no regions have gone off the start of the session */
590 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
591 if ((i->view->region()->position() + total_dx) < 0) {
593 *pending_region_position = _last_frame_position;
604 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
606 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
607 int const n = i->time_axis_view + delta_track;
608 if (n < 0 || n >= int (_time_axis_views.size())) {
609 /* off the top or bottom track */
613 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
614 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
615 /* not a track, or the wrong type */
619 double const l = i->layer + delta_layer;
621 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
622 mode to allow the user to place a region below another on layer 0.
624 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
625 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
626 If it has, the layers will be munged later anyway, so it's ok.
632 /* all regions being dragged are ok with this change */
637 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
639 double delta_layer = 0;
640 int delta_time_axis_view = 0;
642 assert (!_views.empty ());
644 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
646 /* Find the TimeAxisView that the pointer is now over */
647 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
648 TimeAxisView* tv = r.first;
650 if (tv && tv->view()) {
651 double layer = r.second;
653 if (first_move && tv->view()->layer_display() == Stacked) {
654 tv->view()->set_layer_display (Expanded);
657 /* Here's the current pointer position in terms of time axis view and layer */
658 int const current_pointer_time_axis_view = find_time_axis_view (tv);
659 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
661 /* Work out the change in y */
663 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
664 delta_layer = current_pointer_layer - _last_pointer_layer;
667 /* Work out the change in x */
668 framepos_t pending_region_position;
669 double const x_delta = compute_x_delta (event, &pending_region_position);
670 _last_frame_position = pending_region_position;
672 /* Verify change in y */
673 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
674 /* this y movement is not allowed, so do no y movement this time */
675 delta_time_axis_view = 0;
679 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
680 /* haven't reached next snap point, and we're not switching
681 trackviews nor layers. nothing to do.
686 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
688 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
690 RegionView* rv = i->view;
692 if (rv->region()->locked() || rv->region()->video_locked()) {
699 /* reparent the regionview into a group above all
703 ArdourCanvas::Item* rvg = rv->get_canvas_group();
704 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
705 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
706 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
707 /* move the item so that it continues to appear at the
708 same location now that its parent has changed.
710 rvg->move (rv_canvas_offset - dmg_canvas_offset);
713 /* If we have moved tracks, we'll fudge the layer delta so that the
714 region gets moved back onto layer 0 on its new track; this avoids
715 confusion when dragging regions from non-zero layers onto different
718 double this_delta_layer = delta_layer;
719 if (delta_time_axis_view != 0) {
720 this_delta_layer = - i->layer;
727 if (i->time_axis_view >= 0) {
728 track_index = i->time_axis_view + delta_time_axis_view;
730 track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
733 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
737 /* The TimeAxisView that this region is now over */
738 TimeAxisView* current_tv = _time_axis_views[track_index];
740 /* Ensure it is moved from stacked -> expanded if appropriate */
741 if (current_tv->view()->layer_display() == Stacked) {
742 current_tv->view()->set_layer_display (Expanded);
745 /* We're only allowed to go -ve in layer on Expanded views */
746 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
747 this_delta_layer = - i->layer;
751 rv->set_height (current_tv->view()->child_height ());
753 /* Update show/hidden status as the region view may have come from a hidden track,
754 or have moved to one.
756 if (current_tv->hidden ()) {
757 rv->get_canvas_group()->hide ();
759 rv->get_canvas_group()->show ();
762 /* Update the DraggingView */
763 i->time_axis_view = track_index;
764 i->layer += this_delta_layer;
767 _editor->mouse_brush_insert_region (rv, pending_region_position);
771 /* Get the y coordinate of the top of the track that this region is now over */
772 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
774 /* And adjust for the layer that it should be on */
775 StreamView* cv = current_tv->view ();
776 switch (cv->layer_display ()) {
780 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
783 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
787 /* need to get the parent of the regionview
788 * canvas group and get its position in
789 * equivalent coordinate space as the trackview
790 * we are now dragging over.
793 /* Now move the region view */
794 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->canvas_origin().y);
798 /* Only move the region into the empty dropzone at the bottom if the pointer
802 if (current_pointer_y() >= 0) {
804 Coord last_track_bottom_edge;
805 if (!_time_axis_views.empty()) {
806 TimeAxisView* last = _time_axis_views.back();
807 last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
809 last_track_bottom_edge = 0;
812 rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y);
813 i->time_axis_view = -1;
817 } /* foreach region */
819 _total_x_delta += x_delta;
821 if (x_delta != 0 && !_brushing) {
822 show_verbose_cursor_time (_last_frame_position);
825 _last_pointer_time_axis_view += delta_time_axis_view;
826 _last_pointer_layer += delta_layer;
830 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
832 if (_copy && first_move) {
834 /* duplicate the regionview(s) and region(s) */
836 list<DraggingView> new_regionviews;
838 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
840 RegionView* rv = i->view;
841 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
842 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
844 const boost::shared_ptr<const Region> original = rv->region();
845 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
846 region_copy->set_position (original->position());
850 boost::shared_ptr<AudioRegion> audioregion_copy
851 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
853 nrv = new AudioRegionView (*arv, audioregion_copy);
855 boost::shared_ptr<MidiRegion> midiregion_copy
856 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
857 nrv = new MidiRegionView (*mrv, midiregion_copy);
862 nrv->get_canvas_group()->show ();
863 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
865 /* swap _primary to the copy */
867 if (rv == _primary) {
871 /* ..and deselect the one we copied */
873 rv->set_selected (false);
876 if (!new_regionviews.empty()) {
878 /* reflect the fact that we are dragging the copies */
880 _views = new_regionviews;
882 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
886 RegionMotionDrag::motion (event, first_move);
890 RegionMotionDrag::finished (GdkEvent *, bool)
892 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
897 if ((*i)->view()->layer_display() == Expanded) {
898 (*i)->view()->set_layer_display (Stacked);
904 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
906 RegionMotionDrag::finished (ev, movement_occurred);
908 if (!movement_occurred) {
912 if (was_double_click() && !_views.empty()) {
913 DraggingView dv = _views.front();
914 dv.view->show_region_editor ();
921 /* reverse this here so that we have the correct logic to finalize
925 if (Config->get_edit_mode() == Lock) {
926 _x_constrained = !_x_constrained;
929 assert (!_views.empty ());
931 /* We might have hidden region views so that they weren't visible during the drag
932 (when they have been reparented). Now everything can be shown again, as region
933 views are back in their track parent groups.
935 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
936 i->view->get_canvas_group()->show ();
939 bool const changed_position = (_last_frame_position != _primary->region()->position());
940 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
941 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
961 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
965 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
967 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
972 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
973 list<boost::shared_ptr<AudioTrack> > audio_tracks;
974 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
975 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
977 rtav->set_height (original->current_height());
981 ChanCount one_midi_port (DataType::MIDI, 1);
982 list<boost::shared_ptr<MidiTrack> > midi_tracks;
983 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
984 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
986 rtav->set_height (original->current_height());
991 error << _("Could not create new track after region placed in the drop zone") << endmsg;
997 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
999 RegionSelection new_views;
1000 PlaylistSet modified_playlists;
1001 RouteTimeAxisView* new_time_axis_view = 0;
1004 /* all changes were made during motion event handlers */
1006 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1010 _editor->commit_reversible_command ();
1014 if (_x_constrained) {
1015 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1017 _editor->begin_reversible_command (Operations::region_copy);
1020 /* insert the regions into their new playlists */
1021 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1023 RouteTimeAxisView* dest_rtv = 0;
1025 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1031 if (changed_position && !_x_constrained) {
1032 where = i->view->region()->position() - drag_delta;
1034 where = i->view->region()->position();
1037 if (i->time_axis_view < 0) {
1038 if (!new_time_axis_view) {
1039 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1041 dest_rtv = new_time_axis_view;
1043 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1046 if (dest_rtv != 0) {
1047 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1048 if (new_view != 0) {
1049 new_views.push_back (new_view);
1053 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1054 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1057 list<DraggingView>::const_iterator next = i;
1063 /* If we've created new regions either by copying or moving
1064 to a new track, we want to replace the old selection with the new ones
1067 if (new_views.size() > 0) {
1068 _editor->selection->set (new_views);
1071 /* write commands for the accumulated diffs for all our modified playlists */
1072 add_stateful_diff_commands_for_playlists (modified_playlists);
1074 _editor->commit_reversible_command ();
1078 RegionMoveDrag::finished_no_copy (
1079 bool const changed_position,
1080 bool const changed_tracks,
1081 framecnt_t const drag_delta
1084 RegionSelection new_views;
1085 PlaylistSet modified_playlists;
1086 PlaylistSet frozen_playlists;
1087 set<RouteTimeAxisView*> views_to_update;
1088 RouteTimeAxisView* new_time_axis_view = 0;
1091 /* all changes were made during motion event handlers */
1092 _editor->commit_reversible_command ();
1096 if (_x_constrained) {
1097 _editor->begin_reversible_command (_("fixed time region drag"));
1099 _editor->begin_reversible_command (Operations::region_drag);
1102 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1104 RegionView* rv = i->view;
1105 RouteTimeAxisView* dest_rtv = 0;
1107 if (rv->region()->locked() || rv->region()->video_locked()) {
1112 if (i->time_axis_view < 0) {
1113 if (!new_time_axis_view) {
1114 new_time_axis_view = create_destination_time_axis (rv->region(), i->initial_time_axis_view);
1116 dest_rtv = new_time_axis_view;
1118 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1123 double const dest_layer = i->layer;
1125 views_to_update.insert (dest_rtv);
1129 if (changed_position && !_x_constrained) {
1130 where = rv->region()->position() - drag_delta;
1132 where = rv->region()->position();
1135 if (changed_tracks) {
1137 /* insert into new playlist */
1139 RegionView* new_view = insert_region_into_playlist (
1140 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1143 if (new_view == 0) {
1148 new_views.push_back (new_view);
1150 /* remove from old playlist */
1152 /* the region that used to be in the old playlist is not
1153 moved to the new one - we use a copy of it. as a result,
1154 any existing editor for the region should no longer be
1157 rv->hide_region_editor();
1160 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1164 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1166 /* this movement may result in a crossfade being modified, or a layering change,
1167 so we need to get undo data from the playlist as well as the region.
1170 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1172 playlist->clear_changes ();
1175 rv->region()->clear_changes ();
1178 motion on the same track. plonk the previously reparented region
1179 back to its original canvas group (its streamview).
1180 No need to do anything for copies as they are fake regions which will be deleted.
1183 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1184 rv->get_canvas_group()->set_y_position (i->initial_y);
1187 /* just change the model */
1188 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1189 playlist->set_layer (rv->region(), dest_layer);
1192 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1194 r = frozen_playlists.insert (playlist);
1197 playlist->freeze ();
1200 rv->region()->set_position (where);
1202 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1205 if (changed_tracks) {
1207 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1208 was selected in all of them, then removing it from a playlist will have removed all
1209 trace of it from _views (i.e. there were N regions selected, we removed 1,
1210 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1211 corresponding regionview, and _views is now empty).
1213 This could have invalidated any and all iterators into _views.
1215 The heuristic we use here is: if the region selection is empty, break out of the loop
1216 here. if the region selection is not empty, then restart the loop because we know that
1217 we must have removed at least the region(view) we've just been working on as well as any
1218 that we processed on previous iterations.
1220 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1221 we can just iterate.
1225 if (_views.empty()) {
1236 /* If we've created new regions either by copying or moving
1237 to a new track, we want to replace the old selection with the new ones
1240 if (new_views.size() > 0) {
1241 _editor->selection->set (new_views);
1244 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1248 /* write commands for the accumulated diffs for all our modified playlists */
1249 add_stateful_diff_commands_for_playlists (modified_playlists);
1251 _editor->commit_reversible_command ();
1253 /* We have futzed with the layering of canvas items on our streamviews.
1254 If any region changed layer, this will have resulted in the stream
1255 views being asked to set up their region views, and all will be well.
1256 If not, we might now have badly-ordered region views. Ask the StreamViews
1257 involved to sort themselves out, just in case.
1260 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1261 (*i)->view()->playlist_layered ((*i)->track ());
1265 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1266 * @param region Region to remove.
1267 * @param playlist playlist To remove from.
1268 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1269 * that clear_changes () is only called once per playlist.
1272 RegionMoveDrag::remove_region_from_playlist (
1273 boost::shared_ptr<Region> region,
1274 boost::shared_ptr<Playlist> playlist,
1275 PlaylistSet& modified_playlists
1278 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1281 playlist->clear_changes ();
1284 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1288 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1289 * clearing the playlist's diff history first if necessary.
1290 * @param region Region to insert.
1291 * @param dest_rtv Destination RouteTimeAxisView.
1292 * @param dest_layer Destination layer.
1293 * @param where Destination position.
1294 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1295 * that clear_changes () is only called once per playlist.
1296 * @return New RegionView, or 0 if no insert was performed.
1299 RegionMoveDrag::insert_region_into_playlist (
1300 boost::shared_ptr<Region> region,
1301 RouteTimeAxisView* dest_rtv,
1304 PlaylistSet& modified_playlists
1307 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1308 if (!dest_playlist) {
1312 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1313 _new_region_view = 0;
1314 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1316 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1317 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1319 dest_playlist->clear_changes ();
1322 dest_playlist->add_region (region, where);
1324 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1325 dest_playlist->set_layer (region, dest_layer);
1330 assert (_new_region_view);
1332 return _new_region_view;
1336 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1338 _new_region_view = rv;
1342 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1344 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1345 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1347 _editor->session()->add_command (c);
1356 RegionMoveDrag::aborted (bool movement_occurred)
1360 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1367 RegionMotionDrag::aborted (movement_occurred);
1372 RegionMotionDrag::aborted (bool)
1374 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1376 StreamView* sview = (*i)->view();
1379 if (sview->layer_display() == Expanded) {
1380 sview->set_layer_display (Stacked);
1385 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1386 RegionView* rv = i->view;
1387 TimeAxisView* tv = &(rv->get_time_axis_view ());
1388 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1390 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1391 rv->get_canvas_group()->set_y_position (0);
1393 rv->move (-_total_x_delta, 0);
1394 rv->set_height (rtv->view()->child_height ());
1398 /** @param b true to brush, otherwise false.
1399 * @param c true to make copies of the regions being moved, otherwise false.
1401 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1402 : RegionMotionDrag (e, i, p, v, b),
1405 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1408 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1409 if (rtv && rtv->is_track()) {
1410 speed = rtv->track()->speed ();
1413 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1417 RegionMoveDrag::setup_pointer_frame_offset ()
1419 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1422 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1423 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1425 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1427 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1428 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1430 _primary = v->view()->create_region_view (r, false, false);
1432 _primary->get_canvas_group()->show ();
1433 _primary->set_position (pos, 0);
1434 _views.push_back (DraggingView (_primary, this, v));
1436 _last_frame_position = pos;
1438 _item = _primary->get_canvas_group ();
1442 RegionInsertDrag::finished (GdkEvent *, bool)
1444 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1446 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1447 _primary->get_canvas_group()->set_y_position (0);
1449 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1451 _editor->begin_reversible_command (Operations::insert_region);
1452 playlist->clear_changes ();
1453 playlist->add_region (_primary->region (), _last_frame_position);
1455 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1456 if (Config->get_edit_mode() == Ripple) {
1457 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1460 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1461 _editor->commit_reversible_command ();
1469 RegionInsertDrag::aborted (bool)
1476 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1477 : RegionMoveDrag (e, i, p, v, false, false)
1479 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1482 struct RegionSelectionByPosition {
1483 bool operator() (RegionView*a, RegionView* b) {
1484 return a->region()->position () < b->region()->position();
1489 RegionSpliceDrag::motion (GdkEvent* event, bool)
1491 /* Which trackview is this ? */
1493 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1494 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1496 /* The region motion is only processed if the pointer is over
1500 if (!tv || !tv->is_track()) {
1501 /* To make sure we hide the verbose canvas cursor when the mouse is
1502 not held over an audio track.
1504 _editor->verbose_cursor()->hide ();
1507 _editor->verbose_cursor()->show ();
1512 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1518 RegionSelection copy;
1519 _editor->selection->regions.by_position(copy);
1521 framepos_t const pf = adjusted_current_frame (event);
1523 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1525 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1531 boost::shared_ptr<Playlist> playlist;
1533 if ((playlist = atv->playlist()) == 0) {
1537 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1542 if (pf < (*i)->region()->last_frame() + 1) {
1546 if (pf > (*i)->region()->first_frame()) {
1552 playlist->shuffle ((*i)->region(), dir);
1557 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1559 RegionMoveDrag::finished (event, movement_occurred);
1563 RegionSpliceDrag::aborted (bool)
1573 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1575 RegionSelection to_ripple;
1576 TrackViewList tracks;
1577 tracks.push_back (tav);
1578 _editor->get_regions_after (to_ripple, where, tracks);
1580 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1581 if (!exclude.contains (*i)) {
1582 // the selection has already been added to _views
1584 if (drag_in_progress) {
1585 // do the same things that RegionMotionDrag::motion does when
1586 // first_move is true, for the region views that we're adding
1587 // to _views this time
1590 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1591 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1592 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1593 rvg->reparent (_editor->_drag_motion_group);
1595 // we only need to move in the y direction
1596 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1601 _views.push_back (DraggingView (*i, this, tav));
1607 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1610 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1611 // we added all the regions after the selection
1613 std::list<DraggingView>::iterator to_erase = i++;
1614 if (!_editor->selection->regions.contains (to_erase->view)) {
1615 // restore the non-selected regions to their original playlist & positions,
1616 // and then ripple them back by the length of the regions that were dragged away
1617 // do the same things as RegionMotionDrag::aborted
1619 RegionView *rv = to_erase->view;
1620 TimeAxisView* tv = &(rv->get_time_axis_view ());
1621 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1624 // plonk them back onto their own track
1625 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1626 rv->get_canvas_group()->set_y_position (0);
1630 // move the underlying region to match the view
1631 rv->region()->set_position (rv->region()->position() + amount);
1633 // restore the view to match the underlying region's original position
1634 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
1637 rv->set_height (rtv->view()->child_height ());
1638 _views.erase (to_erase);
1644 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const
1646 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) {
1648 return allow_moves_across_tracks;
1656 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1657 : RegionMoveDrag (e, i, p, v, false, false)
1659 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
1660 // compute length of selection
1661 RegionSelection selected_regions = _editor->selection->regions;
1662 selection_length = selected_regions.end_frame() - selected_regions.start();
1664 // we'll only allow dragging to another track in ripple mode if all the regions
1665 // being dragged start off on the same track
1666 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
1669 exclude = new RegionList;
1670 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
1671 exclude->push_back((*i)->region());
1674 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
1675 RegionSelection copy;
1676 selected_regions.by_position(copy); // get selected regions sorted by position into copy
1678 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
1679 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1681 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1682 // find ripple start point on each applicable playlist
1683 RegionView *first_selected_on_this_track = NULL;
1684 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1685 if ((*i)->region()->playlist() == (*pi)) {
1686 // region is on this playlist - it's the first, because they're sorted
1687 first_selected_on_this_track = *i;
1691 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
1692 add_all_after_to_views (
1693 &first_selected_on_this_track->get_time_axis_view(),
1694 first_selected_on_this_track->region()->position() + first_selected_on_this_track->region()->length(),
1695 selected_regions, false);
1698 if (allow_moves_across_tracks) {
1699 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
1707 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
1709 /* Which trackview is this ? */
1711 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1712 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1714 /* The region motion is only processed if the pointer is over
1718 if (!tv || !tv->is_track()) {
1719 /* To make sure we hide the verbose canvas cursor when the mouse is
1720 not held over an audiotrack.
1722 _editor->verbose_cursor()->hide ();
1726 framepos_t where = adjusted_current_frame (event);
1727 assert (where >= 0);
1729 double delta = compute_x_delta (event, &after);
1731 framecnt_t amount = _editor->pixel_to_sample (delta);
1733 if (allow_moves_across_tracks) {
1734 // all the originally selected regions were on the same track
1736 framecnt_t adjust = 0;
1737 if (prev_tav && tv != prev_tav) {
1738 // dragged onto a different track
1739 // remove the unselected regions from _views, restore them to their original positions
1740 // and add the regions after the drop point on the new playlist to _views instead.
1741 // undo the effect of rippling the previous playlist, and include the effect of removing
1742 // the dragged region(s) from this track
1744 remove_unselected_from_views (prev_amount, false);
1745 // ripple previous playlist according to the regions that have been removed onto the new playlist
1746 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
1749 // move just the selected regions
1750 RegionMoveDrag::motion(event, first_move);
1752 // ensure that the ripple operation on the new playlist inserts selection_length time
1753 adjust = selection_length;
1754 // ripple the new current playlist
1755 tv->playlist()->ripple (where, amount+adjust, exclude);
1757 // add regions after point where drag entered this track to subsequent ripples
1758 add_all_after_to_views (tv, where, _editor->selection->regions, true);
1761 // motion on same track
1762 RegionMoveDrag::motion(event, first_move);
1766 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
1767 prev_position = where;
1769 // selection encompasses multiple tracks - just drag
1770 // cross-track drags are forbidden
1771 RegionMoveDrag::motion(event, first_move);
1774 if (!_x_constrained) {
1775 prev_amount += amount;
1778 _last_frame_position = after;
1782 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
1784 if (!movement_occurred) {
1788 _editor->begin_reversible_command(_("Ripple drag"));
1790 // remove the regions being rippled from the dragging view, updating them to
1791 // their new positions
1792 remove_unselected_from_views (prev_amount, true);
1794 if (allow_moves_across_tracks) {
1796 // if regions were dragged across tracks, we've rippled any later
1797 // regions on the track the regions were dragged off, so we need
1798 // to add the original track to the undo record
1799 orig_tav->playlist()->clear_changes();
1800 vector<Command*> cmds;
1801 orig_tav->playlist()->rdiff (cmds);
1802 _editor->session()->add_commands (cmds);
1804 if (prev_tav && prev_tav != orig_tav) {
1805 prev_tav->playlist()->clear_changes();
1806 vector<Command*> cmds;
1807 prev_tav->playlist()->rdiff (cmds);
1808 _editor->session()->add_commands (cmds);
1811 // selection spanned multiple tracks - all will need adding to undo record
1813 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
1814 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1816 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1817 (*pi)->clear_changes();
1818 vector<Command*> cmds;
1819 (*pi)->rdiff (cmds);
1820 _editor->session()->add_commands (cmds);
1824 // other modified playlists are added to undo by RegionMoveDrag::finished()
1825 RegionMoveDrag::finished (event, movement_occurred);
1826 _editor->commit_reversible_command();
1830 RegionRippleDrag::aborted (bool movement_occurred)
1832 RegionMoveDrag::aborted (movement_occurred);
1837 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1839 _view (dynamic_cast<MidiTimeAxisView*> (v))
1841 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1847 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1850 _region = add_midi_region (_view);
1851 _view->playlist()->freeze ();
1854 framepos_t const f = adjusted_current_frame (event);
1855 if (f < grab_frame()) {
1856 _region->set_position (f);
1859 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1860 so that if this region is duplicated, its duplicate starts on
1861 a snap point rather than 1 frame after a snap point. Otherwise things get
1862 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1863 place snapped notes at the start of the region.
1866 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1867 _region->set_length (len < 1 ? 1 : len);
1873 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1875 if (!movement_occurred) {
1876 add_midi_region (_view);
1878 _view->playlist()->thaw ();
1883 RegionCreateDrag::aborted (bool)
1886 _view->playlist()->thaw ();
1892 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1896 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1900 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1902 Gdk::Cursor* cursor;
1903 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1905 float x_fraction = cnote->mouse_x_fraction ();
1907 if (x_fraction > 0.0 && x_fraction < 0.25) {
1908 cursor = _editor->cursors()->left_side_trim;
1910 cursor = _editor->cursors()->right_side_trim;
1913 Drag::start_grab (event, cursor);
1915 region = &cnote->region_view();
1917 double const region_start = region->get_position_pixels();
1918 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1920 if (grab_x() <= middle_point) {
1921 cursor = _editor->cursors()->left_side_trim;
1924 cursor = _editor->cursors()->right_side_trim;
1930 if (event->motion.state & Keyboard::PrimaryModifier) {
1936 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1938 if (ms.size() > 1) {
1939 /* has to be relative, may make no sense otherwise */
1943 /* select this note; if it is already selected, preserve the existing selection,
1944 otherwise make this note the only one selected.
1946 region->note_selected (cnote, cnote->selected ());
1948 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1949 MidiRegionSelection::iterator next;
1952 (*r)->begin_resizing (at_front);
1958 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1960 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1961 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1962 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1964 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1969 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1971 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1972 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1973 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1975 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1980 NoteResizeDrag::aborted (bool)
1982 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1983 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1984 (*r)->abort_resizing ();
1988 AVDraggingView::AVDraggingView (RegionView* v)
1991 initial_position = v->region()->position ();
1994 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1997 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2000 TrackViewList empty;
2002 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2003 std::list<RegionView*> views = rs.by_layer();
2005 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2006 RegionView* rv = (*i);
2007 if (!rv->region()->video_locked()) {
2010 _views.push_back (AVDraggingView (rv));
2015 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2017 Drag::start_grab (event);
2018 if (_editor->session() == 0) {
2022 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2023 _max_backwards_drag = (
2024 ARDOUR_UI::instance()->video_timeline->get_duration()
2025 + ARDOUR_UI::instance()->video_timeline->get_offset()
2026 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2029 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2030 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2031 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2034 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2037 Timecode::Time timecode;
2038 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2039 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);
2040 show_verbose_cursor_text (buf);
2044 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2046 if (_editor->session() == 0) {
2049 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2053 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2054 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2056 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2057 dt = - _max_backwards_drag;
2060 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2061 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2063 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2064 RegionView* rv = i->view;
2065 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2068 rv->region()->clear_changes ();
2069 rv->region()->suspend_property_changes();
2071 rv->region()->set_position(i->initial_position + dt);
2072 rv->region_changed(ARDOUR::Properties::position);
2075 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2076 Timecode::Time timecode;
2077 Timecode::Time timediff;
2079 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2080 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2081 snprintf (buf, sizeof (buf),
2082 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2083 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2084 , _("Video Start:"),
2085 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2087 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2089 show_verbose_cursor_text (buf);
2093 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2095 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2099 if (!movement_occurred || ! _editor->session()) {
2103 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2105 _editor->begin_reversible_command (_("Move Video"));
2107 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2108 ARDOUR_UI::instance()->video_timeline->save_undo();
2109 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2110 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2112 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2113 i->view->drag_end();
2114 i->view->region()->resume_property_changes ();
2116 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2119 _editor->session()->maybe_update_session_range(
2120 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2121 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2125 _editor->commit_reversible_command ();
2129 VideoTimeLineDrag::aborted (bool)
2131 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2134 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2135 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2137 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2138 i->view->region()->resume_property_changes ();
2139 i->view->region()->set_position(i->initial_position);
2143 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2144 : RegionDrag (e, i, p, v)
2145 , _preserve_fade_anchor (preserve_fade_anchor)
2146 , _jump_position_when_done (false)
2148 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2152 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2155 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2156 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2158 if (tv && tv->is_track()) {
2159 speed = tv->track()->speed();
2162 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2163 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2164 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2166 framepos_t const pf = adjusted_current_frame (event);
2168 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2169 /* Move the contents of the region around without changing the region bounds */
2170 _operation = ContentsTrim;
2171 Drag::start_grab (event, _editor->cursors()->trimmer);
2173 /* These will get overridden for a point trim.*/
2174 if (pf < (region_start + region_length/2)) {
2175 /* closer to front */
2176 _operation = StartTrim;
2178 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2179 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2181 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2185 _operation = EndTrim;
2186 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2187 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2189 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2194 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2195 _jump_position_when_done = true;
2198 switch (_operation) {
2200 show_verbose_cursor_time (region_start);
2201 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2202 i->view->trim_front_starting ();
2206 show_verbose_cursor_time (region_end);
2209 show_verbose_cursor_time (pf);
2213 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2214 i->view->region()->suspend_property_changes ();
2219 TrimDrag::motion (GdkEvent* event, bool first_move)
2221 RegionView* rv = _primary;
2224 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2225 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2226 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2227 frameoffset_t frame_delta = 0;
2229 if (tv && tv->is_track()) {
2230 speed = tv->track()->speed();
2233 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2239 switch (_operation) {
2241 trim_type = "Region start trim";
2244 trim_type = "Region end trim";
2247 trim_type = "Region content trim";
2254 _editor->begin_reversible_command (trim_type);
2256 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2257 RegionView* rv = i->view;
2258 rv->enable_display (false);
2259 rv->region()->playlist()->clear_owned_changes ();
2261 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2264 arv->temporarily_hide_envelope ();
2268 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2269 insert_result = _editor->motion_frozen_playlists.insert (pl);
2271 if (insert_result.second) {
2277 bool non_overlap_trim = false;
2279 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2280 non_overlap_trim = true;
2283 /* contstrain trim to fade length */
2284 if (_preserve_fade_anchor) {
2285 switch (_operation) {
2287 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2288 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2290 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2291 if (ar->locked()) continue;
2292 framecnt_t len = ar->fade_in()->back()->when;
2293 if (len < dt) dt = min(dt, len);
2297 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2298 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2300 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2301 if (ar->locked()) continue;
2302 framecnt_t len = ar->fade_out()->back()->when;
2303 if (len < -dt) dt = max(dt, -len);
2312 switch (_operation) {
2314 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2315 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2316 if (changed && _preserve_fade_anchor) {
2317 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2319 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2320 framecnt_t len = ar->fade_in()->back()->when;
2321 framecnt_t diff = ar->first_frame() - i->initial_position;
2322 framepos_t new_length = len - diff;
2323 i->anchored_fade_length = min (ar->length(), new_length);
2324 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2325 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2332 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2333 bool changed = i->view->trim_end (i->initial_end + 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_out()->back()->when;
2339 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
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, false /*END*/ );
2343 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2351 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2353 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2354 i->view->move_contents (frame_delta);
2360 switch (_operation) {
2362 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2365 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2368 // show_verbose_cursor_time (frame_delta);
2375 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2377 if (movement_occurred) {
2378 motion (event, false);
2380 if (_operation == StartTrim) {
2381 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2383 /* This must happen before the region's StatefulDiffCommand is created, as it may
2384 `correct' (ahem) the region's _start from being negative to being zero. It
2385 needs to be zero in the undo record.
2387 i->view->trim_front_ending ();
2389 if (_preserve_fade_anchor) {
2390 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2392 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2393 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2394 ar->set_fade_in_length(i->anchored_fade_length);
2395 ar->set_fade_in_active(true);
2398 if (_jump_position_when_done) {
2399 i->view->region()->set_position (i->initial_position);
2402 } else if (_operation == EndTrim) {
2403 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2404 if (_preserve_fade_anchor) {
2405 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2407 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2408 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2409 ar->set_fade_out_length(i->anchored_fade_length);
2410 ar->set_fade_out_active(true);
2413 if (_jump_position_when_done) {
2414 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2419 if (!_views.empty()) {
2420 if (_operation == StartTrim) {
2421 _editor->maybe_locate_with_edit_preroll(
2422 _views.begin()->view->region()->position());
2424 if (_operation == EndTrim) {
2425 _editor->maybe_locate_with_edit_preroll(
2426 _views.begin()->view->region()->position() +
2427 _views.begin()->view->region()->length());
2431 if (!_editor->selection->selected (_primary)) {
2432 _primary->thaw_after_trim ();
2435 set<boost::shared_ptr<Playlist> > diffed_playlists;
2437 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2438 i->view->thaw_after_trim ();
2439 i->view->enable_display (true);
2441 /* Trimming one region may affect others on the playlist, so we need
2442 to get undo Commands from the whole playlist rather than just the
2443 region. Use diffed_playlists to make sure we don't diff a given
2444 playlist more than once.
2446 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2447 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2448 vector<Command*> cmds;
2450 _editor->session()->add_commands (cmds);
2451 diffed_playlists.insert (p);
2456 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2460 _editor->motion_frozen_playlists.clear ();
2461 _editor->commit_reversible_command();
2464 /* no mouse movement */
2465 _editor->point_trim (event, adjusted_current_frame (event));
2468 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2469 if (_operation == StartTrim) {
2470 i->view->trim_front_ending ();
2473 i->view->region()->resume_property_changes ();
2478 TrimDrag::aborted (bool movement_occurred)
2480 /* Our motion method is changing model state, so use the Undo system
2481 to cancel. Perhaps not ideal, as this will leave an Undo point
2482 behind which may be slightly odd from the user's point of view.
2487 if (movement_occurred) {
2491 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2492 i->view->region()->resume_property_changes ();
2497 TrimDrag::setup_pointer_frame_offset ()
2499 list<DraggingView>::iterator i = _views.begin ();
2500 while (i != _views.end() && i->view != _primary) {
2504 if (i == _views.end()) {
2508 switch (_operation) {
2510 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2513 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2520 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2524 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2525 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2530 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2532 Drag::start_grab (event, cursor);
2533 show_verbose_cursor_time (adjusted_current_frame(event));
2537 MeterMarkerDrag::setup_pointer_frame_offset ()
2539 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2543 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2545 if (!_marker->meter().movable()) {
2551 // create a dummy marker for visual representation of moving the
2552 // section, because whether its a copy or not, we're going to
2553 // leave or lose the original marker (leave if its a copy; lose if its
2554 // not, because we'll remove it from the map).
2556 MeterSection section (_marker->meter());
2558 if (!section.movable()) {
2563 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2565 _marker = new MeterMarker (
2567 *_editor->meter_group,
2568 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2570 *new MeterSection (_marker->meter())
2573 /* use the new marker for the grab */
2574 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2577 TempoMap& map (_editor->session()->tempo_map());
2578 /* get current state */
2579 before_state = &map.get_state();
2580 /* remove the section while we drag it */
2581 map.remove_meter (section, true);
2585 framepos_t const pf = adjusted_current_frame (event);
2586 _marker->set_position (pf);
2587 show_verbose_cursor_time (pf);
2591 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2593 if (!movement_occurred) {
2594 if (was_double_click()) {
2595 _editor->edit_meter_marker (*_marker);
2600 if (!_marker->meter().movable()) {
2604 motion (event, false);
2606 Timecode::BBT_Time when;
2608 TempoMap& map (_editor->session()->tempo_map());
2609 map.bbt_time (last_pointer_frame(), when);
2611 if (_copy == true) {
2612 _editor->begin_reversible_command (_("copy meter mark"));
2613 XMLNode &before = map.get_state();
2614 map.add_meter (_marker->meter(), when);
2615 XMLNode &after = map.get_state();
2616 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2617 _editor->commit_reversible_command ();
2620 _editor->begin_reversible_command (_("move meter mark"));
2622 /* we removed it before, so add it back now */
2624 map.add_meter (_marker->meter(), when);
2625 XMLNode &after = map.get_state();
2626 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2627 _editor->commit_reversible_command ();
2630 // delete the dummy marker we used for visual representation while moving.
2631 // a new visual marker will show up automatically.
2636 MeterMarkerDrag::aborted (bool moved)
2638 _marker->set_position (_marker->meter().frame ());
2641 TempoMap& map (_editor->session()->tempo_map());
2642 /* we removed it before, so add it back now */
2643 map.add_meter (_marker->meter(), _marker->meter().frame());
2644 // delete the dummy marker we used for visual representation while moving.
2645 // a new visual marker will show up automatically.
2650 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2654 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2656 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2661 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2663 Drag::start_grab (event, cursor);
2664 show_verbose_cursor_time (adjusted_current_frame (event));
2668 TempoMarkerDrag::setup_pointer_frame_offset ()
2670 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2674 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2676 if (!_marker->tempo().movable()) {
2682 // create a dummy marker for visual representation of moving the
2683 // section, because whether its a copy or not, we're going to
2684 // leave or lose the original marker (leave if its a copy; lose if its
2685 // not, because we'll remove it from the map).
2687 // create a dummy marker for visual representation of moving the copy.
2688 // The actual copying is not done before we reach the finish callback.
2691 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2693 TempoSection section (_marker->tempo());
2695 _marker = new TempoMarker (
2697 *_editor->tempo_group,
2698 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2700 *new TempoSection (_marker->tempo())
2703 /* use the new marker for the grab */
2704 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2707 TempoMap& map (_editor->session()->tempo_map());
2708 /* get current state */
2709 before_state = &map.get_state();
2710 /* remove the section while we drag it */
2711 map.remove_tempo (section, true);
2715 framepos_t const pf = adjusted_current_frame (event);
2716 _marker->set_position (pf);
2717 show_verbose_cursor_time (pf);
2721 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2723 if (!movement_occurred) {
2724 if (was_double_click()) {
2725 _editor->edit_tempo_marker (*_marker);
2730 if (!_marker->tempo().movable()) {
2734 motion (event, false);
2736 TempoMap& map (_editor->session()->tempo_map());
2737 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2738 Timecode::BBT_Time when;
2740 map.bbt_time (beat_time, when);
2742 if (_copy == true) {
2743 _editor->begin_reversible_command (_("copy tempo mark"));
2744 XMLNode &before = map.get_state();
2745 map.add_tempo (_marker->tempo(), when);
2746 XMLNode &after = map.get_state();
2747 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2748 _editor->commit_reversible_command ();
2751 _editor->begin_reversible_command (_("move tempo mark"));
2752 /* we removed it before, so add it back now */
2753 map.add_tempo (_marker->tempo(), when);
2754 XMLNode &after = map.get_state();
2755 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2756 _editor->commit_reversible_command ();
2759 // delete the dummy marker we used for visual representation while moving.
2760 // a new visual marker will show up automatically.
2765 TempoMarkerDrag::aborted (bool moved)
2767 _marker->set_position (_marker->tempo().frame());
2769 TempoMap& map (_editor->session()->tempo_map());
2770 /* we removed it before, so add it back now */
2771 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2772 // delete the dummy marker we used for visual representation while moving.
2773 // a new visual marker will show up automatically.
2778 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2779 : Drag (e, &c.track_canvas_item(), false)
2783 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2786 /** Do all the things we do when dragging the playhead to make it look as though
2787 * we have located, without actually doing the locate (because that would cause
2788 * the diskstream buffers to be refilled, which is too slow).
2791 CursorDrag::fake_locate (framepos_t t)
2793 _editor->playhead_cursor->set_position (t);
2795 Session* s = _editor->session ();
2796 if (s->timecode_transmission_suspended ()) {
2797 framepos_t const f = _editor->playhead_cursor->current_frame ();
2798 /* This is asynchronous so it will be sent "now"
2800 s->send_mmc_locate (f);
2801 /* These are synchronous and will be sent during the next
2804 s->queue_full_time_code ();
2805 s->queue_song_position_pointer ();
2808 show_verbose_cursor_time (t);
2809 _editor->UpdateAllTransportClocks (t);
2813 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2815 Drag::start_grab (event, c);
2817 _grab_zoom = _editor->samples_per_pixel;
2819 framepos_t where = _editor->canvas_event_sample (event);
2821 _editor->snap_to_with_modifier (where, event);
2823 _editor->_dragging_playhead = true;
2825 Session* s = _editor->session ();
2827 /* grab the track canvas item as well */
2829 _cursor.track_canvas_item().grab();
2832 if (_was_rolling && _stop) {
2836 if (s->is_auditioning()) {
2837 s->cancel_audition ();
2841 if (AudioEngine::instance()->connected()) {
2843 /* do this only if we're the engine is connected
2844 * because otherwise this request will never be
2845 * serviced and we'll busy wait forever. likewise,
2846 * notice if we are disconnected while waiting for the
2847 * request to be serviced.
2850 s->request_suspend_timecode_transmission ();
2851 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2852 /* twiddle our thumbs */
2857 fake_locate (where);
2861 CursorDrag::motion (GdkEvent* event, bool)
2863 framepos_t const adjusted_frame = adjusted_current_frame (event);
2864 if (adjusted_frame != last_pointer_frame()) {
2865 fake_locate (adjusted_frame);
2870 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2872 _editor->_dragging_playhead = false;
2874 _cursor.track_canvas_item().ungrab();
2876 if (!movement_occurred && _stop) {
2880 motion (event, false);
2882 Session* s = _editor->session ();
2884 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2885 _editor->_pending_locate_request = true;
2886 s->request_resume_timecode_transmission ();
2891 CursorDrag::aborted (bool)
2893 _cursor.track_canvas_item().ungrab();
2895 if (_editor->_dragging_playhead) {
2896 _editor->session()->request_resume_timecode_transmission ();
2897 _editor->_dragging_playhead = false;
2900 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2903 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2904 : RegionDrag (e, i, p, v)
2906 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2910 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2912 Drag::start_grab (event, cursor);
2914 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2915 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2917 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2921 FadeInDrag::setup_pointer_frame_offset ()
2923 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2924 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2925 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2929 FadeInDrag::motion (GdkEvent* event, bool)
2931 framecnt_t fade_length;
2933 framepos_t const pos = adjusted_current_frame (event);
2935 boost::shared_ptr<Region> region = _primary->region ();
2937 if (pos < (region->position() + 64)) {
2938 fade_length = 64; // this should be a minimum defined somewhere
2939 } else if (pos > region->last_frame()) {
2940 fade_length = region->length();
2942 fade_length = pos - region->position();
2945 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2947 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2953 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2956 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2960 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2962 if (!movement_occurred) {
2966 framecnt_t fade_length;
2968 framepos_t const pos = adjusted_current_frame (event);
2970 boost::shared_ptr<Region> region = _primary->region ();
2972 if (pos < (region->position() + 64)) {
2973 fade_length = 64; // this should be a minimum defined somewhere
2974 } else if (pos > region->last_frame()) {
2975 fade_length = region->length();
2977 fade_length = pos - region->position();
2980 _editor->begin_reversible_command (_("change fade in length"));
2982 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2984 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2990 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2991 XMLNode &before = alist->get_state();
2993 tmp->audio_region()->set_fade_in_length (fade_length);
2994 tmp->audio_region()->set_fade_in_active (true);
2996 XMLNode &after = alist->get_state();
2997 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3000 _editor->commit_reversible_command ();
3004 FadeInDrag::aborted (bool)
3006 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3007 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3013 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3017 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3018 : RegionDrag (e, i, p, v)
3020 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3024 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3026 Drag::start_grab (event, cursor);
3028 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3029 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3031 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3035 FadeOutDrag::setup_pointer_frame_offset ()
3037 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3038 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3039 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3043 FadeOutDrag::motion (GdkEvent* event, bool)
3045 framecnt_t fade_length;
3047 framepos_t const pos = adjusted_current_frame (event);
3049 boost::shared_ptr<Region> region = _primary->region ();
3051 if (pos > (region->last_frame() - 64)) {
3052 fade_length = 64; // this should really be a minimum fade defined somewhere
3054 else if (pos < region->position()) {
3055 fade_length = region->length();
3058 fade_length = region->last_frame() - pos;
3061 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3063 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3069 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3072 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3076 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3078 if (!movement_occurred) {
3082 framecnt_t fade_length;
3084 framepos_t const pos = adjusted_current_frame (event);
3086 boost::shared_ptr<Region> region = _primary->region ();
3088 if (pos > (region->last_frame() - 64)) {
3089 fade_length = 64; // this should really be a minimum fade defined somewhere
3091 else if (pos < region->position()) {
3092 fade_length = region->length();
3095 fade_length = region->last_frame() - pos;
3098 _editor->begin_reversible_command (_("change fade out length"));
3100 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3102 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3108 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3109 XMLNode &before = alist->get_state();
3111 tmp->audio_region()->set_fade_out_length (fade_length);
3112 tmp->audio_region()->set_fade_out_active (true);
3114 XMLNode &after = alist->get_state();
3115 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3118 _editor->commit_reversible_command ();
3122 FadeOutDrag::aborted (bool)
3124 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3125 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3131 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3135 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3138 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3140 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3143 _points.push_back (ArdourCanvas::Duple (0, 0));
3144 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3147 MarkerDrag::~MarkerDrag ()
3149 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3154 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3156 location = new Location (*l);
3157 markers.push_back (m);
3162 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3164 Drag::start_grab (event, cursor);
3168 Location *location = _editor->find_location_from_marker (_marker, is_start);
3169 _editor->_dragging_edit_point = true;
3171 update_item (location);
3173 // _drag_line->show();
3174 // _line->raise_to_top();
3177 show_verbose_cursor_time (location->start());
3179 show_verbose_cursor_time (location->end());
3182 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3185 case Selection::Toggle:
3186 /* we toggle on the button release */
3188 case Selection::Set:
3189 if (!_editor->selection->selected (_marker)) {
3190 _editor->selection->set (_marker);
3193 case Selection::Extend:
3195 Locations::LocationList ll;
3196 list<Marker*> to_add;
3198 _editor->selection->markers.range (s, e);
3199 s = min (_marker->position(), s);
3200 e = max (_marker->position(), e);
3203 if (e < max_framepos) {
3206 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3207 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3208 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3211 to_add.push_back (lm->start);
3214 to_add.push_back (lm->end);
3218 if (!to_add.empty()) {
3219 _editor->selection->add (to_add);
3223 case Selection::Add:
3224 _editor->selection->add (_marker);
3228 /* Set up copies for us to manipulate during the drag
3231 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3233 Location* l = _editor->find_location_from_marker (*i, is_start);
3240 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3242 /* range: check that the other end of the range isn't
3245 CopiedLocationInfo::iterator x;
3246 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3247 if (*(*x).location == *l) {
3251 if (x == _copied_locations.end()) {
3252 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3254 (*x).markers.push_back (*i);
3255 (*x).move_both = true;
3263 MarkerDrag::setup_pointer_frame_offset ()
3266 Location *location = _editor->find_location_from_marker (_marker, is_start);
3267 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3271 MarkerDrag::motion (GdkEvent* event, bool)
3273 framecnt_t f_delta = 0;
3275 bool move_both = false;
3276 Location *real_location;
3277 Location *copy_location = 0;
3279 framepos_t const newframe = adjusted_current_frame (event);
3280 framepos_t next = newframe;
3282 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3286 CopiedLocationInfo::iterator x;
3288 /* find the marker we're dragging, and compute the delta */
3290 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3292 copy_location = (*x).location;
3294 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3296 /* this marker is represented by this
3297 * CopiedLocationMarkerInfo
3300 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3305 if (real_location->is_mark()) {
3306 f_delta = newframe - copy_location->start();
3310 switch (_marker->type()) {
3311 case Marker::SessionStart:
3312 case Marker::RangeStart:
3313 case Marker::LoopStart:
3314 case Marker::PunchIn:
3315 f_delta = newframe - copy_location->start();
3318 case Marker::SessionEnd:
3319 case Marker::RangeEnd:
3320 case Marker::LoopEnd:
3321 case Marker::PunchOut:
3322 f_delta = newframe - copy_location->end();
3325 /* what kind of marker is this ? */
3334 if (x == _copied_locations.end()) {
3335 /* hmm, impossible - we didn't find the dragged marker */
3339 /* now move them all */
3341 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3343 copy_location = x->location;
3345 /* call this to find out if its the start or end */
3347 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3351 if (real_location->locked()) {
3355 if (copy_location->is_mark()) {
3359 copy_location->set_start (copy_location->start() + f_delta);
3363 framepos_t new_start = copy_location->start() + f_delta;
3364 framepos_t new_end = copy_location->end() + f_delta;
3366 if (is_start) { // start-of-range marker
3368 if (move_both || (*x).move_both) {
3369 copy_location->set_start (new_start);
3370 copy_location->set_end (new_end);
3371 } else if (new_start < copy_location->end()) {
3372 copy_location->set_start (new_start);
3373 } else if (newframe > 0) {
3374 _editor->snap_to (next, 1, true);
3375 copy_location->set_end (next);
3376 copy_location->set_start (newframe);
3379 } else { // end marker
3381 if (move_both || (*x).move_both) {
3382 copy_location->set_end (new_end);
3383 copy_location->set_start (new_start);
3384 } else if (new_end > copy_location->start()) {
3385 copy_location->set_end (new_end);
3386 } else if (newframe > 0) {
3387 _editor->snap_to (next, -1, true);
3388 copy_location->set_start (next);
3389 copy_location->set_end (newframe);
3394 update_item (copy_location);
3396 /* now lookup the actual GUI items used to display this
3397 * location and move them to wherever the copy of the location
3398 * is now. This means that the logic in ARDOUR::Location is
3399 * still enforced, even though we are not (yet) modifying
3400 * the real Location itself.
3403 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3406 lm->set_position (copy_location->start(), copy_location->end());
3411 assert (!_copied_locations.empty());
3413 show_verbose_cursor_time (newframe);
3417 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3419 if (!movement_occurred) {
3421 if (was_double_click()) {
3422 _editor->rename_marker (_marker);
3426 /* just a click, do nothing but finish
3427 off the selection process
3430 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3433 case Selection::Set:
3434 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3435 _editor->selection->set (_marker);
3439 case Selection::Toggle:
3440 /* we toggle on the button release, click only */
3441 _editor->selection->toggle (_marker);
3444 case Selection::Extend:
3445 case Selection::Add:
3452 _editor->_dragging_edit_point = false;
3454 _editor->begin_reversible_command ( _("move marker") );
3455 XMLNode &before = _editor->session()->locations()->get_state();
3457 MarkerSelection::iterator i;
3458 CopiedLocationInfo::iterator x;
3461 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3462 x != _copied_locations.end() && i != _editor->selection->markers.end();
3465 Location * location = _editor->find_location_from_marker (*i, is_start);
3469 if (location->locked()) {
3473 if (location->is_mark()) {
3474 location->set_start (((*x).location)->start());
3476 location->set (((*x).location)->start(), ((*x).location)->end());
3481 XMLNode &after = _editor->session()->locations()->get_state();
3482 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3483 _editor->commit_reversible_command ();
3487 MarkerDrag::aborted (bool)
3493 MarkerDrag::update_item (Location*)
3498 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3500 _cumulative_x_drag (0),
3501 _cumulative_y_drag (0)
3503 if (_zero_gain_fraction < 0.0) {
3504 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3507 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3509 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3515 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3517 Drag::start_grab (event, _editor->cursors()->fader);
3519 // start the grab at the center of the control point so
3520 // the point doesn't 'jump' to the mouse after the first drag
3521 _fixed_grab_x = _point->get_x();
3522 _fixed_grab_y = _point->get_y();
3524 float const fraction = 1 - (_point->get_y() / _point->line().height());
3526 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3528 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3530 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3532 if (!_point->can_slide ()) {
3533 _x_constrained = true;
3538 ControlPointDrag::motion (GdkEvent* event, bool)
3540 double dx = _drags->current_pointer_x() - last_pointer_x();
3541 double dy = current_pointer_y() - last_pointer_y();
3543 if (event->button.state & Keyboard::SecondaryModifier) {
3548 /* coordinate in pixels relative to the start of the region (for region-based automation)
3549 or track (for track-based automation) */
3550 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3551 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3553 // calculate zero crossing point. back off by .01 to stay on the
3554 // positive side of zero
3555 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3557 // make sure we hit zero when passing through
3558 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3562 if (_x_constrained) {
3565 if (_y_constrained) {
3569 _cumulative_x_drag = cx - _fixed_grab_x;
3570 _cumulative_y_drag = cy - _fixed_grab_y;
3574 cy = min ((double) _point->line().height(), cy);
3576 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3578 if (!_x_constrained) {
3579 _editor->snap_to_with_modifier (cx_frames, event);
3582 cx_frames = min (cx_frames, _point->line().maximum_time());
3584 float const fraction = 1.0 - (cy / _point->line().height());
3586 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3588 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3592 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3594 if (!movement_occurred) {
3598 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3599 _editor->reset_point_selection ();
3603 motion (event, false);
3606 _point->line().end_drag (_pushing, _final_index);
3607 _editor->session()->commit_reversible_command ();
3611 ControlPointDrag::aborted (bool)
3613 _point->line().reset ();
3617 ControlPointDrag::active (Editing::MouseMode m)
3619 if (m == Editing::MouseGain) {
3620 /* always active in mouse gain */
3624 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3625 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3628 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3631 _cumulative_y_drag (0)
3633 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3637 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3639 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3642 _item = &_line->grab_item ();
3644 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3645 origin, and ditto for y.
3648 double cx = event->button.x;
3649 double cy = event->button.y;
3651 _line->parent_group().canvas_to_item (cx, cy);
3653 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3658 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3659 /* no adjacent points */
3663 Drag::start_grab (event, _editor->cursors()->fader);
3665 /* store grab start in parent frame */
3670 double fraction = 1.0 - (cy / _line->height());
3672 _line->start_drag_line (before, after, fraction);
3674 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3678 LineDrag::motion (GdkEvent* event, bool)
3680 double dy = current_pointer_y() - last_pointer_y();
3682 if (event->button.state & Keyboard::SecondaryModifier) {
3686 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3688 _cumulative_y_drag = cy - _fixed_grab_y;
3691 cy = min ((double) _line->height(), cy);
3693 double const fraction = 1.0 - (cy / _line->height());
3696 /* we are ignoring x position for this drag, so we can just pass in anything */
3697 _line->drag_motion (0, fraction, true, false, ignored);
3699 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3703 LineDrag::finished (GdkEvent* event, bool movement_occured)
3705 if (movement_occured) {
3706 motion (event, false);
3707 _line->end_drag (false, 0);
3709 /* add a new control point on the line */
3711 AutomationTimeAxisView* atv;
3713 _line->end_drag (false, 0);
3715 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3716 framepos_t where = _editor->window_event_sample (event, 0, 0);
3717 atv->add_automation_event (event, where, event->button.y, false);
3721 _editor->session()->commit_reversible_command ();
3725 LineDrag::aborted (bool)
3730 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3733 _cumulative_x_drag (0)
3735 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3739 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3741 Drag::start_grab (event);
3743 _line = reinterpret_cast<Line*> (_item);
3746 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3748 double cx = event->button.x;
3749 double cy = event->button.y;
3751 _item->parent()->canvas_to_item (cx, cy);
3753 /* store grab start in parent frame */
3754 _region_view_grab_x = cx;
3756 _before = *(float*) _item->get_data ("position");
3758 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3760 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3764 FeatureLineDrag::motion (GdkEvent*, bool)
3766 double dx = _drags->current_pointer_x() - last_pointer_x();
3768 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3770 _cumulative_x_drag += dx;
3772 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3781 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3783 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3785 float *pos = new float;
3788 _line->set_data ("position", pos);
3794 FeatureLineDrag::finished (GdkEvent*, bool)
3796 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3797 _arv->update_transient(_before, _before);
3801 FeatureLineDrag::aborted (bool)
3806 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3808 , _vertical_only (false)
3810 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3814 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3816 Drag::start_grab (event);
3817 show_verbose_cursor_time (adjusted_current_frame (event));
3821 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3828 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3830 framepos_t grab = grab_frame ();
3831 if (Config->get_rubberbanding_snaps_to_grid ()) {
3832 _editor->snap_to_with_modifier (grab, event);
3835 /* base start and end on initial click position */
3845 if (current_pointer_y() < grab_y()) {
3846 y1 = current_pointer_y();
3849 y2 = current_pointer_y();
3853 if (start != end || y1 != y2) {
3855 double x1 = _editor->sample_to_pixel (start);
3856 double x2 = _editor->sample_to_pixel (end);
3857 const double min_dimension = 2.0;
3859 if (_vertical_only) {
3860 /* fixed 10 pixel width */
3864 x2 = min (x1 - min_dimension, x2);
3866 x2 = max (x1 + min_dimension, x2);
3871 y2 = min (y1 - min_dimension, y2);
3873 y2 = max (y1 + min_dimension, y2);
3876 /* translate rect into item space and set */
3878 ArdourCanvas::Rect r (x1, y1, x2, y2);
3880 /* this drag is a _trackview_only == true drag, so the y1 and
3881 * y2 (computed using current_pointer_y() and grab_y()) will be
3882 * relative to the top of the trackview group). The
3883 * rubberband rect has the same parent/scroll offset as the
3884 * the trackview group, so we can use the "r" rect directly
3885 * to set the shape of the rubberband.
3888 _editor->rubberband_rect->set (r);
3889 _editor->rubberband_rect->show();
3890 _editor->rubberband_rect->raise_to_top();
3892 show_verbose_cursor_time (pf);
3894 do_select_things (event, true);
3899 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3904 if (grab_frame() < last_pointer_frame()) {
3906 x2 = last_pointer_frame ();
3909 x1 = last_pointer_frame ();
3915 if (current_pointer_y() < grab_y()) {
3916 y1 = current_pointer_y();
3919 y2 = current_pointer_y();
3923 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3927 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3929 if (movement_occurred) {
3931 motion (event, false);
3932 do_select_things (event, false);
3938 bool do_deselect = true;
3939 MidiTimeAxisView* mtv;
3941 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3943 if (_editor->selection->empty()) {
3944 /* nothing selected */
3945 add_midi_region (mtv);
3946 do_deselect = false;
3950 /* do not deselect if Primary or Tertiary (toggle-select or
3951 * extend-select are pressed.
3954 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3955 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3962 _editor->rubberband_rect->hide();
3966 RubberbandSelectDrag::aborted (bool)
3968 _editor->rubberband_rect->hide ();
3971 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3972 : RegionDrag (e, i, p, v)
3974 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3978 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3980 Drag::start_grab (event, cursor);
3982 show_verbose_cursor_time (adjusted_current_frame (event));
3986 TimeFXDrag::motion (GdkEvent* event, bool)
3988 RegionView* rv = _primary;
3989 StreamView* cv = rv->get_time_axis_view().view ();
3991 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3992 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3993 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3995 framepos_t const pf = adjusted_current_frame (event);
3997 if (pf > rv->region()->position()) {
3998 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4001 show_verbose_cursor_time (pf);
4005 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4007 _primary->get_time_axis_view().hide_timestretch ();
4009 if (!movement_occurred) {
4013 if (last_pointer_frame() < _primary->region()->position()) {
4014 /* backwards drag of the left edge - not usable */
4018 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4020 float percentage = (double) newlen / (double) _primary->region()->length();
4022 #ifndef USE_RUBBERBAND
4023 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4024 if (_primary->region()->data_type() == DataType::AUDIO) {
4025 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4029 if (!_editor->get_selection().regions.empty()) {
4030 /* primary will already be included in the selection, and edit
4031 group shared editing will propagate selection across
4032 equivalent regions, so just use the current region
4036 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4037 error << _("An error occurred while executing time stretch operation") << endmsg;
4043 TimeFXDrag::aborted (bool)
4045 _primary->get_time_axis_view().hide_timestretch ();
4048 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4051 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4055 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4057 Drag::start_grab (event);
4061 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4063 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4067 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4069 if (movement_occurred && _editor->session()) {
4070 /* make sure we stop */
4071 _editor->session()->request_transport_speed (0.0);
4076 ScrubDrag::aborted (bool)
4081 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4086 , _original_pointer_time_axis (-1)
4087 , _last_pointer_time_axis (-1)
4088 , _time_selection_at_start (!_editor->get_selection().time.empty())
4090 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4092 if (_time_selection_at_start) {
4093 start_at_start = _editor->get_selection().time.start();
4094 end_at_start = _editor->get_selection().time.end_frame();
4099 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4101 if (_editor->session() == 0) {
4105 Gdk::Cursor* cursor = 0;
4107 switch (_operation) {
4108 case CreateSelection:
4109 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4114 cursor = _editor->cursors()->selector;
4115 Drag::start_grab (event, cursor);
4118 case SelectionStartTrim:
4119 if (_editor->clicked_axisview) {
4120 _editor->clicked_axisview->order_selection_trims (_item, true);
4122 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4125 case SelectionEndTrim:
4126 if (_editor->clicked_axisview) {
4127 _editor->clicked_axisview->order_selection_trims (_item, false);
4129 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4133 Drag::start_grab (event, cursor);
4136 case SelectionExtend:
4137 Drag::start_grab (event, cursor);
4141 if (_operation == SelectionMove) {
4142 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4144 show_verbose_cursor_time (adjusted_current_frame (event));
4147 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4151 SelectionDrag::setup_pointer_frame_offset ()
4153 switch (_operation) {
4154 case CreateSelection:
4155 _pointer_frame_offset = 0;
4158 case SelectionStartTrim:
4160 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4163 case SelectionEndTrim:
4164 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4167 case SelectionExtend:
4173 SelectionDrag::motion (GdkEvent* event, bool first_move)
4175 framepos_t start = 0;
4177 framecnt_t length = 0;
4178 framecnt_t distance = 0;
4180 framepos_t const pending_position = adjusted_current_frame (event);
4182 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4186 switch (_operation) {
4187 case CreateSelection:
4189 framepos_t grab = grab_frame ();
4192 grab = adjusted_current_frame (event, false);
4193 if (grab < pending_position) {
4194 _editor->snap_to (grab, -1);
4196 _editor->snap_to (grab, 1);
4200 if (pending_position < grab) {
4201 start = pending_position;
4204 end = pending_position;
4208 /* first drag: Either add to the selection
4209 or create a new selection
4216 /* adding to the selection */
4217 _editor->set_selected_track_as_side_effect (Selection::Add);
4218 _editor->clicked_selection = _editor->selection->add (start, end);
4225 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4226 _editor->set_selected_track_as_side_effect (Selection::Set);
4229 _editor->clicked_selection = _editor->selection->set (start, end);
4233 /* select all tracks within the rectangle that we've marked out so far */
4234 TrackViewList to_be_added_to_selection;
4235 TrackViewList to_be_removed_from_selection;
4236 TrackViewList& all_tracks (_editor->track_views);
4238 ArdourCanvas::Coord const top = grab_y();
4239 ArdourCanvas::Coord const bottom = current_pointer_y();
4241 if (top >= 0 && bottom >= 0) {
4243 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4245 if ((*i)->covered_by_y_range (top, bottom)) {
4246 if (!(*i)->get_selected()) {
4247 to_be_added_to_selection.push_back (*i);
4250 if ((*i)->get_selected()) {
4251 to_be_removed_from_selection.push_back (*i);
4256 if (!to_be_added_to_selection.empty()) {
4257 _editor->selection->add (to_be_added_to_selection);
4260 if (!to_be_removed_from_selection.empty()) {
4261 _editor->selection->remove (to_be_removed_from_selection);
4267 case SelectionStartTrim:
4269 start = _editor->selection->time[_editor->clicked_selection].start;
4270 end = _editor->selection->time[_editor->clicked_selection].end;
4272 if (pending_position > end) {
4275 start = pending_position;
4279 case SelectionEndTrim:
4281 start = _editor->selection->time[_editor->clicked_selection].start;
4282 end = _editor->selection->time[_editor->clicked_selection].end;
4284 if (pending_position < start) {
4287 end = pending_position;
4294 start = _editor->selection->time[_editor->clicked_selection].start;
4295 end = _editor->selection->time[_editor->clicked_selection].end;
4297 length = end - start;
4298 distance = pending_position - start;
4299 start = pending_position;
4300 _editor->snap_to (start);
4302 end = start + length;
4306 case SelectionExtend:
4311 switch (_operation) {
4313 if (_time_selection_at_start) {
4314 _editor->selection->move_time (distance);
4318 _editor->selection->replace (_editor->clicked_selection, start, end);
4322 if (_operation == SelectionMove) {
4323 show_verbose_cursor_time(start);
4325 show_verbose_cursor_time(pending_position);
4330 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4332 Session* s = _editor->session();
4334 if (movement_occurred) {
4335 motion (event, false);
4336 /* XXX this is not object-oriented programming at all. ick */
4337 if (_editor->selection->time.consolidate()) {
4338 _editor->selection->TimeChanged ();
4341 /* XXX what if its a music time selection? */
4343 if ( s->get_play_range() && s->transport_rolling() ) {
4344 s->request_play_range (&_editor->selection->time, true);
4346 if (Config->get_always_play_range() && !s->transport_rolling()) {
4347 s->request_locate (_editor->get_selection().time.start());
4353 /* just a click, no pointer movement.
4356 if (_operation == SelectionExtend) {
4357 if (_time_selection_at_start) {
4358 framepos_t pos = adjusted_current_frame (event, false);
4359 framepos_t start = min (pos, start_at_start);
4360 framepos_t end = max (pos, end_at_start);
4361 _editor->selection->set (start, end);
4364 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4365 if (_editor->clicked_selection) {
4366 _editor->selection->remove (_editor->clicked_selection);
4369 if (!_editor->clicked_selection) {
4370 _editor->selection->clear_time();
4375 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4376 _editor->selection->set (_editor->clicked_axisview);
4379 if (s && s->get_play_range () && s->transport_rolling()) {
4380 s->request_stop (false, false);
4385 _editor->stop_canvas_autoscroll ();
4386 _editor->clicked_selection = 0;
4390 SelectionDrag::aborted (bool)
4395 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4396 : Drag (e, i, false),
4400 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4402 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4403 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4404 physical_screen_height (_editor->get_window())));
4405 _drag_rect->hide ();
4407 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4408 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4412 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4414 if (_editor->session() == 0) {
4418 Gdk::Cursor* cursor = 0;
4420 if (!_editor->temp_location) {
4421 _editor->temp_location = new Location (*_editor->session());
4424 switch (_operation) {
4425 case CreateRangeMarker:
4426 case CreateTransportMarker:
4427 case CreateCDMarker:
4429 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4434 cursor = _editor->cursors()->selector;
4438 Drag::start_grab (event, cursor);
4440 show_verbose_cursor_time (adjusted_current_frame (event));
4444 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4446 framepos_t start = 0;
4448 ArdourCanvas::Rectangle *crect;
4450 switch (_operation) {
4451 case CreateRangeMarker:
4452 crect = _editor->range_bar_drag_rect;
4454 case CreateTransportMarker:
4455 crect = _editor->transport_bar_drag_rect;
4457 case CreateCDMarker:
4458 crect = _editor->cd_marker_bar_drag_rect;
4461 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4466 framepos_t const pf = adjusted_current_frame (event);
4468 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4469 framepos_t grab = grab_frame ();
4470 _editor->snap_to (grab);
4472 if (pf < grab_frame()) {
4480 /* first drag: Either add to the selection
4481 or create a new selection.
4486 _editor->temp_location->set (start, end);
4490 update_item (_editor->temp_location);
4492 //_drag_rect->raise_to_top();
4498 _editor->temp_location->set (start, end);
4500 double x1 = _editor->sample_to_pixel (start);
4501 double x2 = _editor->sample_to_pixel (end);
4505 update_item (_editor->temp_location);
4508 show_verbose_cursor_time (pf);
4513 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4515 Location * newloc = 0;
4519 if (movement_occurred) {
4520 motion (event, false);
4523 switch (_operation) {
4524 case CreateRangeMarker:
4525 case CreateCDMarker:
4527 _editor->begin_reversible_command (_("new range marker"));
4528 XMLNode &before = _editor->session()->locations()->get_state();
4529 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4530 if (_operation == CreateCDMarker) {
4531 flags = Location::IsRangeMarker | Location::IsCDMarker;
4532 _editor->cd_marker_bar_drag_rect->hide();
4535 flags = Location::IsRangeMarker;
4536 _editor->range_bar_drag_rect->hide();
4538 newloc = new Location (
4539 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4542 _editor->session()->locations()->add (newloc, true);
4543 XMLNode &after = _editor->session()->locations()->get_state();
4544 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4545 _editor->commit_reversible_command ();
4549 case CreateTransportMarker:
4550 // popup menu to pick loop or punch
4551 _editor->new_transport_marker_context_menu (&event->button, _item);
4557 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4559 if (_operation == CreateTransportMarker) {
4561 /* didn't drag, so just locate */
4563 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4565 } else if (_operation == CreateCDMarker) {
4567 /* didn't drag, but mark is already created so do
4570 } else { /* operation == CreateRangeMarker */
4576 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4578 if (end == max_framepos) {
4579 end = _editor->session()->current_end_frame ();
4582 if (start == max_framepos) {
4583 start = _editor->session()->current_start_frame ();
4586 switch (_editor->mouse_mode) {
4588 /* find the two markers on either side and then make the selection from it */
4589 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4593 /* find the two markers on either side of the click and make the range out of it */
4594 _editor->selection->set (start, end);
4603 _editor->stop_canvas_autoscroll ();
4607 RangeMarkerBarDrag::aborted (bool)
4613 RangeMarkerBarDrag::update_item (Location* location)
4615 double const x1 = _editor->sample_to_pixel (location->start());
4616 double const x2 = _editor->sample_to_pixel (location->end());
4618 _drag_rect->set_x0 (x1);
4619 _drag_rect->set_x1 (x2);
4622 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4626 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4630 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4632 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4633 Drag::start_grab (event, _editor->cursors()->zoom_out);
4636 Drag::start_grab (event, _editor->cursors()->zoom_in);
4640 show_verbose_cursor_time (adjusted_current_frame (event));
4644 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4649 framepos_t const pf = adjusted_current_frame (event);
4651 framepos_t grab = grab_frame ();
4652 _editor->snap_to_with_modifier (grab, event);
4654 /* base start and end on initial click position */
4666 _editor->zoom_rect->show();
4667 _editor->zoom_rect->raise_to_top();
4670 _editor->reposition_zoom_rect(start, end);
4672 show_verbose_cursor_time (pf);
4677 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4679 if (movement_occurred) {
4680 motion (event, false);
4682 if (grab_frame() < last_pointer_frame()) {
4683 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4685 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4688 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4689 _editor->tav_zoom_step (_zoom_out);
4691 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4695 _editor->zoom_rect->hide();
4699 MouseZoomDrag::aborted (bool)
4701 _editor->zoom_rect->hide ();
4704 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4706 , _cumulative_dx (0)
4707 , _cumulative_dy (0)
4709 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4711 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4713 _region = &_primary->region_view ();
4714 _note_height = _region->midi_stream_view()->note_height ();
4718 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4720 Drag::start_grab (event);
4722 if (!(_was_selected = _primary->selected())) {
4724 /* tertiary-click means extend selection - we'll do that on button release,
4725 so don't add it here, because otherwise we make it hard to figure
4726 out the "extend-to" range.
4729 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4732 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4735 _region->note_selected (_primary, true);
4737 _region->unique_select (_primary);
4743 /** @return Current total drag x change in frames */
4745 NoteDrag::total_dx () const
4748 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4750 /* primary note time */
4751 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4753 /* new time of the primary note in session frames */
4754 frameoffset_t st = n + dx;
4756 framepos_t const rp = _region->region()->position ();
4758 /* prevent the note being dragged earlier than the region's position */
4761 /* snap and return corresponding delta */
4762 return _region->snap_frame_to_frame (st - rp) + rp - n;
4765 /** @return Current total drag y change in note number */
4767 NoteDrag::total_dy () const
4769 MidiStreamView* msv = _region->midi_stream_view ();
4770 double const y = _region->midi_view()->y_position ();
4771 /* new current note */
4772 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4774 n = max (msv->lowest_note(), n);
4775 n = min (msv->highest_note(), n);
4776 /* and work out delta */
4777 return n - msv->y_to_note (grab_y() - y);
4781 NoteDrag::motion (GdkEvent *, bool)
4783 /* Total change in x and y since the start of the drag */
4784 frameoffset_t const dx = total_dx ();
4785 int8_t const dy = total_dy ();
4787 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4788 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4789 double const tdy = -dy * _note_height - _cumulative_dy;
4792 _cumulative_dx += tdx;
4793 _cumulative_dy += tdy;
4795 int8_t note_delta = total_dy();
4797 _region->move_selection (tdx, tdy, note_delta);
4799 /* the new note value may be the same as the old one, but we
4800 * don't know what that means because the selection may have
4801 * involved more than one note and we might be doing something
4802 * odd with them. so show the note value anyway, always.
4806 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4808 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4809 (int) floor ((double)new_note));
4811 show_verbose_cursor_text (buf);
4816 NoteDrag::finished (GdkEvent* ev, bool moved)
4819 /* no motion - select note */
4821 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4822 _editor->current_mouse_mode() == Editing::MouseDraw) {
4824 if (_was_selected) {
4825 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4827 _region->note_deselected (_primary);
4830 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4831 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4833 if (!extend && !add && _region->selection_size() > 1) {
4834 _region->unique_select (_primary);
4835 } else if (extend) {
4836 _region->note_selected (_primary, true, true);
4838 /* it was added during button press */
4843 _region->note_dropped (_primary, total_dx(), total_dy());
4848 NoteDrag::aborted (bool)
4853 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4854 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4855 : Drag (editor, atv->base_item ())
4857 , _nothing_to_drag (false)
4859 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4860 y_origin = atv->y_position();
4861 setup (atv->lines ());
4864 /** Make an AutomationRangeDrag for region gain lines */
4865 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4866 : Drag (editor, rv->get_canvas_group ())
4868 , _nothing_to_drag (false)
4870 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4872 list<boost::shared_ptr<AutomationLine> > lines;
4873 lines.push_back (rv->get_gain_line ());
4874 y_origin = rv->get_time_axis_view().y_position();
4878 /** @param lines AutomationLines to drag.
4879 * @param offset Offset from the session start to the points in the AutomationLines.
4882 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4884 /* find the lines that overlap the ranges being dragged */
4885 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4886 while (i != lines.end ()) {
4887 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4890 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4892 /* check this range against all the AudioRanges that we are using */
4893 list<AudioRange>::const_iterator k = _ranges.begin ();
4894 while (k != _ranges.end()) {
4895 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4901 /* add it to our list if it overlaps at all */
4902 if (k != _ranges.end()) {
4907 _lines.push_back (n);
4913 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4917 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4919 return 1.0 - ((global_y - y_origin) / line->height());
4923 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4925 Drag::start_grab (event, cursor);
4927 /* Get line states before we start changing things */
4928 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4929 i->state = &i->line->get_state ();
4930 i->original_fraction = y_fraction (i->line, current_pointer_y());
4933 if (_ranges.empty()) {
4935 /* No selected time ranges: drag all points */
4936 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4937 uint32_t const N = i->line->npoints ();
4938 for (uint32_t j = 0; j < N; ++j) {
4939 i->points.push_back (i->line->nth (j));
4945 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4947 framecnt_t const half = (i->start + i->end) / 2;
4949 /* find the line that this audio range starts in */
4950 list<Line>::iterator j = _lines.begin();
4951 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4955 if (j != _lines.end()) {
4956 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4958 /* j is the line that this audio range starts in; fade into it;
4959 64 samples length plucked out of thin air.
4962 framepos_t a = i->start + 64;
4967 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4968 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4970 the_list->add (p, the_list->eval (p));
4971 the_list->add (q, the_list->eval (q));
4974 /* same thing for the end */
4977 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4981 if (j != _lines.end()) {
4982 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4984 /* j is the line that this audio range starts in; fade out of it;
4985 64 samples length plucked out of thin air.
4988 framepos_t b = i->end - 64;
4993 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4994 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4996 the_list->add (p, the_list->eval (p));
4997 the_list->add (q, the_list->eval (q));
5001 _nothing_to_drag = true;
5003 /* Find all the points that should be dragged and put them in the relevant
5004 points lists in the Line structs.
5007 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5009 uint32_t const N = i->line->npoints ();
5010 for (uint32_t j = 0; j < N; ++j) {
5012 /* here's a control point on this line */
5013 ControlPoint* p = i->line->nth (j);
5014 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5016 /* see if it's inside a range */
5017 list<AudioRange>::const_iterator k = _ranges.begin ();
5018 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5022 if (k != _ranges.end()) {
5023 /* dragging this point */
5024 _nothing_to_drag = false;
5025 i->points.push_back (p);
5031 if (_nothing_to_drag) {
5035 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5036 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5041 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5043 if (_nothing_to_drag) {
5047 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5048 float const f = y_fraction (l->line, current_pointer_y());
5049 /* we are ignoring x position for this drag, so we can just pass in anything */
5051 l->line->drag_motion (0, f, true, false, ignored);
5052 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5057 AutomationRangeDrag::finished (GdkEvent* event, bool)
5059 if (_nothing_to_drag) {
5063 motion (event, false);
5064 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5065 i->line->end_drag (false, 0);
5068 _editor->session()->commit_reversible_command ();
5072 AutomationRangeDrag::aborted (bool)
5074 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5079 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5081 , initial_time_axis_view (itav)
5083 /* note that time_axis_view may be null if the regionview was created
5084 * as part of a copy operation.
5086 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5087 layer = v->region()->layer ();
5088 initial_y = v->get_canvas_group()->position().y;
5089 initial_playlist = v->region()->playlist ();
5090 initial_position = v->region()->position ();
5091 initial_end = v->region()->position () + v->region()->length ();
5094 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5095 : Drag (e, i->canvas_item ())
5098 , _cumulative_dx (0)
5100 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5101 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5106 PatchChangeDrag::motion (GdkEvent* ev, bool)
5108 framepos_t f = adjusted_current_frame (ev);
5109 boost::shared_ptr<Region> r = _region_view->region ();
5110 f = max (f, r->position ());
5111 f = min (f, r->last_frame ());
5113 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5114 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5115 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5116 _cumulative_dx = dxu;
5120 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5122 if (!movement_occurred) {
5126 boost::shared_ptr<Region> r (_region_view->region ());
5127 framepos_t f = adjusted_current_frame (ev);
5128 f = max (f, r->position ());
5129 f = min (f, r->last_frame ());
5131 _region_view->move_patch_change (
5133 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5138 PatchChangeDrag::aborted (bool)
5140 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5144 PatchChangeDrag::setup_pointer_frame_offset ()
5146 boost::shared_ptr<Region> region = _region_view->region ();
5147 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5150 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5151 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5158 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5160 framepos_t const p = _region_view->region()->position ();
5161 double const y = _region_view->midi_view()->y_position ();
5163 x1 = max ((framepos_t) 0, x1 - p);
5164 x2 = max ((framepos_t) 0, x2 - p);
5165 y1 = max (0.0, y1 - y);
5166 y2 = max (0.0, y2 - y);
5168 _region_view->update_drag_selection (
5169 _editor->sample_to_pixel (x1),
5170 _editor->sample_to_pixel (x2),
5173 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5178 MidiRubberbandSelectDrag::deselect_things ()
5183 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5184 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5187 _vertical_only = true;
5191 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5193 double const y = _region_view->midi_view()->y_position ();
5195 y1 = max (0.0, y1 - y);
5196 y2 = max (0.0, y2 - y);
5198 _region_view->update_vertical_drag_selection (
5201 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5206 MidiVerticalSelectDrag::deselect_things ()
5211 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5212 : RubberbandSelectDrag (e, i)
5218 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5220 if (drag_in_progress) {
5221 /* We just want to select things at the end of the drag, not during it */
5225 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5227 _editor->begin_reversible_command (_("rubberband selection"));
5228 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5229 _editor->commit_reversible_command ();
5233 EditorRubberbandSelectDrag::deselect_things ()
5235 if (!getenv("ARDOUR_SAE")) {
5236 _editor->selection->clear_tracks();
5238 _editor->selection->clear_regions();
5239 _editor->selection->clear_points ();
5240 _editor->selection->clear_lines ();
5243 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5251 NoteCreateDrag::~NoteCreateDrag ()
5257 NoteCreateDrag::grid_frames (framepos_t t) const
5260 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
5265 return _region_view->region_beats_to_region_frames (grid_beats);
5269 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5271 Drag::start_grab (event, cursor);
5273 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5275 framepos_t pf = _drags->current_pointer_frame ();
5276 framecnt_t const g = grid_frames (pf);
5278 /* Hack so that we always snap to the note that we are over, instead of snapping
5279 to the next one if we're more than halfway through the one we're over.
5281 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5285 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5287 MidiStreamView* sv = _region_view->midi_stream_view ();
5288 double const x = _editor->sample_to_pixel (_note[0]);
5289 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5291 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5292 _drag_rect->set_outline_all ();
5293 _drag_rect->set_outline_color (0xffffff99);
5294 _drag_rect->set_fill_color (0xffffff66);
5298 NoteCreateDrag::motion (GdkEvent* event, bool)
5300 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5301 double const x = _editor->sample_to_pixel (_note[1]);
5302 if (_note[1] > _note[0]) {
5303 _drag_rect->set_x1 (x);
5305 _drag_rect->set_x0 (x);
5310 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5312 if (!had_movement) {
5316 framepos_t const start = min (_note[0], _note[1]);
5317 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5319 framecnt_t const g = grid_frames (start);
5320 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5322 if (_editor->snap_mode() == SnapNormal && length < g) {
5323 length = g - one_tick;
5326 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5328 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5332 NoteCreateDrag::y_to_region (double y) const
5335 _region_view->get_canvas_group()->canvas_to_item (x, y);
5340 NoteCreateDrag::aborted (bool)
5345 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5350 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5354 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5356 Drag::start_grab (event, cursor);
5360 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5366 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5369 distance = _drags->current_pointer_x() - grab_x();
5370 len = ar->fade_in()->back()->when;
5372 distance = grab_x() - _drags->current_pointer_x();
5373 len = ar->fade_out()->back()->when;
5376 /* how long should it be ? */
5378 new_length = len + _editor->pixel_to_sample (distance);
5380 /* now check with the region that this is legal */
5382 new_length = ar->verify_xfade_bounds (new_length, start);
5385 arv->reset_fade_in_shape_width (ar, new_length);
5387 arv->reset_fade_out_shape_width (ar, new_length);
5392 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5398 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5401 distance = _drags->current_pointer_x() - grab_x();
5402 len = ar->fade_in()->back()->when;
5404 distance = grab_x() - _drags->current_pointer_x();
5405 len = ar->fade_out()->back()->when;
5408 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5410 _editor->begin_reversible_command ("xfade trim");
5411 ar->playlist()->clear_owned_changes ();
5414 ar->set_fade_in_length (new_length);
5416 ar->set_fade_out_length (new_length);
5419 /* Adjusting the xfade may affect other regions in the playlist, so we need
5420 to get undo Commands from the whole playlist rather than just the
5424 vector<Command*> cmds;
5425 ar->playlist()->rdiff (cmds);
5426 _editor->session()->add_commands (cmds);
5427 _editor->commit_reversible_command ();
5432 CrossfadeEdgeDrag::aborted (bool)
5435 arv->redraw_start_xfade ();
5437 arv->redraw_end_xfade ();