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)
1576 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1578 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1579 RegionSelection to_ripple;
1580 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1581 if ((*i)->position() >= where) {
1582 to_ripple.push_back (rtv->view()->find_view(*i));
1586 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1587 if (!exclude.contains (*i)) {
1588 // the selection has already been added to _views
1590 if (drag_in_progress) {
1591 // do the same things that RegionMotionDrag::motion does when
1592 // first_move is true, for the region views that we're adding
1593 // to _views this time
1596 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1597 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1598 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1599 rvg->reparent (_editor->_drag_motion_group);
1601 // we only need to move in the y direction
1602 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1607 _views.push_back (DraggingView (*i, this, tav));
1613 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1616 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1617 // we added all the regions after the selection
1619 std::list<DraggingView>::iterator to_erase = i++;
1620 if (!_editor->selection->regions.contains (to_erase->view)) {
1621 // restore the non-selected regions to their original playlist & positions,
1622 // and then ripple them back by the length of the regions that were dragged away
1623 // do the same things as RegionMotionDrag::aborted
1625 RegionView *rv = to_erase->view;
1626 TimeAxisView* tv = &(rv->get_time_axis_view ());
1627 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1630 // plonk them back onto their own track
1631 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1632 rv->get_canvas_group()->set_y_position (0);
1636 // move the underlying region to match the view
1637 rv->region()->set_position (rv->region()->position() + amount);
1639 // restore the view to match the underlying region's original position
1640 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
1643 rv->set_height (rtv->view()->child_height ());
1644 _views.erase (to_erase);
1650 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const
1652 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) {
1654 return allow_moves_across_tracks;
1662 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1663 : RegionMoveDrag (e, i, p, v, false, false)
1665 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
1666 // compute length of selection
1667 RegionSelection selected_regions = _editor->selection->regions;
1668 selection_length = selected_regions.end_frame() - selected_regions.start();
1670 // we'll only allow dragging to another track in ripple mode if all the regions
1671 // being dragged start off on the same track
1672 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
1675 exclude = new RegionList;
1676 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
1677 exclude->push_back((*i)->region());
1680 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
1681 RegionSelection copy;
1682 selected_regions.by_position(copy); // get selected regions sorted by position into copy
1684 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
1685 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1687 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1688 // find ripple start point on each applicable playlist
1689 RegionView *first_selected_on_this_track = NULL;
1690 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1691 if ((*i)->region()->playlist() == (*pi)) {
1692 // region is on this playlist - it's the first, because they're sorted
1693 first_selected_on_this_track = *i;
1697 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
1698 add_all_after_to_views (
1699 &first_selected_on_this_track->get_time_axis_view(),
1700 first_selected_on_this_track->region()->position(),
1701 selected_regions, false);
1704 if (allow_moves_across_tracks) {
1705 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
1713 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
1715 /* Which trackview is this ? */
1717 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1718 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1720 /* The region motion is only processed if the pointer is over
1724 if (!tv || !tv->is_track()) {
1725 /* To make sure we hide the verbose canvas cursor when the mouse is
1726 not held over an audiotrack.
1728 _editor->verbose_cursor()->hide ();
1732 framepos_t where = adjusted_current_frame (event);
1733 assert (where >= 0);
1735 double delta = compute_x_delta (event, &after);
1737 framecnt_t amount = _editor->pixel_to_sample (delta);
1739 if (allow_moves_across_tracks) {
1740 // all the originally selected regions were on the same track
1742 framecnt_t adjust = 0;
1743 if (prev_tav && tv != prev_tav) {
1744 // dragged onto a different track
1745 // remove the unselected regions from _views, restore them to their original positions
1746 // and add the regions after the drop point on the new playlist to _views instead.
1747 // undo the effect of rippling the previous playlist, and include the effect of removing
1748 // the dragged region(s) from this track
1750 remove_unselected_from_views (prev_amount, false);
1751 // ripple previous playlist according to the regions that have been removed onto the new playlist
1752 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
1755 // move just the selected regions
1756 RegionMoveDrag::motion(event, first_move);
1758 // ensure that the ripple operation on the new playlist inserts selection_length time
1759 adjust = selection_length;
1760 // ripple the new current playlist
1761 tv->playlist()->ripple (where, amount+adjust, exclude);
1763 // add regions after point where drag entered this track to subsequent ripples
1764 add_all_after_to_views (tv, where, _editor->selection->regions, true);
1767 // motion on same track
1768 RegionMoveDrag::motion(event, first_move);
1772 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
1773 prev_position = where;
1775 // selection encompasses multiple tracks - just drag
1776 // cross-track drags are forbidden
1777 RegionMoveDrag::motion(event, first_move);
1780 if (!_x_constrained) {
1781 prev_amount += amount;
1784 _last_frame_position = after;
1788 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
1790 if (!movement_occurred) {
1794 _editor->begin_reversible_command(_("Ripple drag"));
1796 // remove the regions being rippled from the dragging view, updating them to
1797 // their new positions
1798 remove_unselected_from_views (prev_amount, true);
1800 if (allow_moves_across_tracks) {
1802 // if regions were dragged across tracks, we've rippled any later
1803 // regions on the track the regions were dragged off, so we need
1804 // to add the original track to the undo record
1805 orig_tav->playlist()->clear_changes();
1806 vector<Command*> cmds;
1807 orig_tav->playlist()->rdiff (cmds);
1808 _editor->session()->add_commands (cmds);
1810 if (prev_tav && prev_tav != orig_tav) {
1811 prev_tav->playlist()->clear_changes();
1812 vector<Command*> cmds;
1813 prev_tav->playlist()->rdiff (cmds);
1814 _editor->session()->add_commands (cmds);
1817 // selection spanned multiple tracks - all will need adding to undo record
1819 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
1820 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1822 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1823 (*pi)->clear_changes();
1824 vector<Command*> cmds;
1825 (*pi)->rdiff (cmds);
1826 _editor->session()->add_commands (cmds);
1830 // other modified playlists are added to undo by RegionMoveDrag::finished()
1831 RegionMoveDrag::finished (event, movement_occurred);
1832 _editor->commit_reversible_command();
1836 RegionRippleDrag::aborted (bool movement_occurred)
1838 RegionMoveDrag::aborted (movement_occurred);
1843 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1845 _view (dynamic_cast<MidiTimeAxisView*> (v))
1847 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1853 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1856 _region = add_midi_region (_view);
1857 _view->playlist()->freeze ();
1860 framepos_t const f = adjusted_current_frame (event);
1861 if (f < grab_frame()) {
1862 _region->set_position (f);
1865 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1866 so that if this region is duplicated, its duplicate starts on
1867 a snap point rather than 1 frame after a snap point. Otherwise things get
1868 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1869 place snapped notes at the start of the region.
1872 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1873 _region->set_length (len < 1 ? 1 : len);
1879 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1881 if (!movement_occurred) {
1882 add_midi_region (_view);
1884 _view->playlist()->thaw ();
1889 RegionCreateDrag::aborted (bool)
1892 _view->playlist()->thaw ();
1898 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1902 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1906 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1908 Gdk::Cursor* cursor;
1909 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1911 float x_fraction = cnote->mouse_x_fraction ();
1913 if (x_fraction > 0.0 && x_fraction < 0.25) {
1914 cursor = _editor->cursors()->left_side_trim;
1916 cursor = _editor->cursors()->right_side_trim;
1919 Drag::start_grab (event, cursor);
1921 region = &cnote->region_view();
1923 double const region_start = region->get_position_pixels();
1924 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1926 if (grab_x() <= middle_point) {
1927 cursor = _editor->cursors()->left_side_trim;
1930 cursor = _editor->cursors()->right_side_trim;
1936 if (event->motion.state & Keyboard::PrimaryModifier) {
1942 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1944 if (ms.size() > 1) {
1945 /* has to be relative, may make no sense otherwise */
1949 /* select this note; if it is already selected, preserve the existing selection,
1950 otherwise make this note the only one selected.
1952 region->note_selected (cnote, cnote->selected ());
1954 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1955 MidiRegionSelection::iterator next;
1958 (*r)->begin_resizing (at_front);
1964 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1966 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1967 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1968 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1970 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1975 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1977 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1978 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1979 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1981 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1986 NoteResizeDrag::aborted (bool)
1988 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1989 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1990 (*r)->abort_resizing ();
1994 AVDraggingView::AVDraggingView (RegionView* v)
1997 initial_position = v->region()->position ();
2000 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2003 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2006 TrackViewList empty;
2008 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2009 std::list<RegionView*> views = rs.by_layer();
2011 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2012 RegionView* rv = (*i);
2013 if (!rv->region()->video_locked()) {
2016 _views.push_back (AVDraggingView (rv));
2021 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2023 Drag::start_grab (event);
2024 if (_editor->session() == 0) {
2028 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2029 _max_backwards_drag = (
2030 ARDOUR_UI::instance()->video_timeline->get_duration()
2031 + ARDOUR_UI::instance()->video_timeline->get_offset()
2032 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2035 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2036 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2037 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2040 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2043 Timecode::Time timecode;
2044 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2045 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);
2046 show_verbose_cursor_text (buf);
2050 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2052 if (_editor->session() == 0) {
2055 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2059 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2060 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2062 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2063 dt = - _max_backwards_drag;
2066 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2067 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2069 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2070 RegionView* rv = i->view;
2071 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2074 rv->region()->clear_changes ();
2075 rv->region()->suspend_property_changes();
2077 rv->region()->set_position(i->initial_position + dt);
2078 rv->region_changed(ARDOUR::Properties::position);
2081 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2082 Timecode::Time timecode;
2083 Timecode::Time timediff;
2085 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2086 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2087 snprintf (buf, sizeof (buf),
2088 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2089 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2090 , _("Video Start:"),
2091 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2093 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2095 show_verbose_cursor_text (buf);
2099 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2101 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2105 if (!movement_occurred || ! _editor->session()) {
2109 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2111 _editor->begin_reversible_command (_("Move Video"));
2113 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2114 ARDOUR_UI::instance()->video_timeline->save_undo();
2115 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2116 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2118 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2119 i->view->drag_end();
2120 i->view->region()->resume_property_changes ();
2122 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2125 _editor->session()->maybe_update_session_range(
2126 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2127 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2131 _editor->commit_reversible_command ();
2135 VideoTimeLineDrag::aborted (bool)
2137 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2140 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2141 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2143 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2144 i->view->region()->resume_property_changes ();
2145 i->view->region()->set_position(i->initial_position);
2149 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2150 : RegionDrag (e, i, p, v)
2151 , _preserve_fade_anchor (preserve_fade_anchor)
2152 , _jump_position_when_done (false)
2154 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2158 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2161 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2162 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2164 if (tv && tv->is_track()) {
2165 speed = tv->track()->speed();
2168 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2169 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2170 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2172 framepos_t const pf = adjusted_current_frame (event);
2174 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2175 /* Move the contents of the region around without changing the region bounds */
2176 _operation = ContentsTrim;
2177 Drag::start_grab (event, _editor->cursors()->trimmer);
2179 /* These will get overridden for a point trim.*/
2180 if (pf < (region_start + region_length/2)) {
2181 /* closer to front */
2182 _operation = StartTrim;
2184 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2185 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2187 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2191 _operation = EndTrim;
2192 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2193 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2195 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2200 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2201 _jump_position_when_done = true;
2204 switch (_operation) {
2206 show_verbose_cursor_time (region_start);
2207 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2208 i->view->trim_front_starting ();
2212 show_verbose_cursor_time (region_end);
2215 show_verbose_cursor_time (pf);
2219 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2220 i->view->region()->suspend_property_changes ();
2225 TrimDrag::motion (GdkEvent* event, bool first_move)
2227 RegionView* rv = _primary;
2230 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2231 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2232 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2233 frameoffset_t frame_delta = 0;
2235 if (tv && tv->is_track()) {
2236 speed = tv->track()->speed();
2239 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2245 switch (_operation) {
2247 trim_type = "Region start trim";
2250 trim_type = "Region end trim";
2253 trim_type = "Region content trim";
2260 _editor->begin_reversible_command (trim_type);
2262 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2263 RegionView* rv = i->view;
2264 rv->enable_display (false);
2265 rv->region()->playlist()->clear_owned_changes ();
2267 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2270 arv->temporarily_hide_envelope ();
2274 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2275 insert_result = _editor->motion_frozen_playlists.insert (pl);
2277 if (insert_result.second) {
2283 bool non_overlap_trim = false;
2285 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2286 non_overlap_trim = true;
2289 /* contstrain trim to fade length */
2290 if (_preserve_fade_anchor) {
2291 switch (_operation) {
2293 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2294 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2296 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2297 if (ar->locked()) continue;
2298 framecnt_t len = ar->fade_in()->back()->when;
2299 if (len < dt) dt = min(dt, len);
2303 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2304 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2306 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2307 if (ar->locked()) continue;
2308 framecnt_t len = ar->fade_out()->back()->when;
2309 if (len < -dt) dt = max(dt, -len);
2318 switch (_operation) {
2320 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2321 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2322 if (changed && _preserve_fade_anchor) {
2323 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2325 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2326 framecnt_t len = ar->fade_in()->back()->when;
2327 framecnt_t diff = ar->first_frame() - i->initial_position;
2328 framepos_t new_length = len - diff;
2329 i->anchored_fade_length = min (ar->length(), new_length);
2330 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2331 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2338 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2339 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2340 if (changed && _preserve_fade_anchor) {
2341 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2343 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2344 framecnt_t len = ar->fade_out()->back()->when;
2345 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2346 framepos_t new_length = len + diff;
2347 i->anchored_fade_length = min (ar->length(), new_length);
2348 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2349 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2357 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2359 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2360 i->view->move_contents (frame_delta);
2366 switch (_operation) {
2368 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2371 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2374 // show_verbose_cursor_time (frame_delta);
2381 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2383 if (movement_occurred) {
2384 motion (event, false);
2386 if (_operation == StartTrim) {
2387 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2389 /* This must happen before the region's StatefulDiffCommand is created, as it may
2390 `correct' (ahem) the region's _start from being negative to being zero. It
2391 needs to be zero in the undo record.
2393 i->view->trim_front_ending ();
2395 if (_preserve_fade_anchor) {
2396 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2398 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2399 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2400 ar->set_fade_in_length(i->anchored_fade_length);
2401 ar->set_fade_in_active(true);
2404 if (_jump_position_when_done) {
2405 i->view->region()->set_position (i->initial_position);
2408 } else if (_operation == EndTrim) {
2409 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2410 if (_preserve_fade_anchor) {
2411 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2413 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2414 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2415 ar->set_fade_out_length(i->anchored_fade_length);
2416 ar->set_fade_out_active(true);
2419 if (_jump_position_when_done) {
2420 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2425 if (!_views.empty()) {
2426 if (_operation == StartTrim) {
2427 _editor->maybe_locate_with_edit_preroll(
2428 _views.begin()->view->region()->position());
2430 if (_operation == EndTrim) {
2431 _editor->maybe_locate_with_edit_preroll(
2432 _views.begin()->view->region()->position() +
2433 _views.begin()->view->region()->length());
2437 if (!_editor->selection->selected (_primary)) {
2438 _primary->thaw_after_trim ();
2441 set<boost::shared_ptr<Playlist> > diffed_playlists;
2443 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2444 i->view->thaw_after_trim ();
2445 i->view->enable_display (true);
2447 /* Trimming one region may affect others on the playlist, so we need
2448 to get undo Commands from the whole playlist rather than just the
2449 region. Use diffed_playlists to make sure we don't diff a given
2450 playlist more than once.
2452 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2453 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2454 vector<Command*> cmds;
2456 _editor->session()->add_commands (cmds);
2457 diffed_playlists.insert (p);
2462 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2466 _editor->motion_frozen_playlists.clear ();
2467 _editor->commit_reversible_command();
2470 /* no mouse movement */
2471 _editor->point_trim (event, adjusted_current_frame (event));
2474 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2475 if (_operation == StartTrim) {
2476 i->view->trim_front_ending ();
2479 i->view->region()->resume_property_changes ();
2484 TrimDrag::aborted (bool movement_occurred)
2486 /* Our motion method is changing model state, so use the Undo system
2487 to cancel. Perhaps not ideal, as this will leave an Undo point
2488 behind which may be slightly odd from the user's point of view.
2493 if (movement_occurred) {
2497 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2498 i->view->region()->resume_property_changes ();
2503 TrimDrag::setup_pointer_frame_offset ()
2505 list<DraggingView>::iterator i = _views.begin ();
2506 while (i != _views.end() && i->view != _primary) {
2510 if (i == _views.end()) {
2514 switch (_operation) {
2516 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2519 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2526 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2530 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2531 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2536 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2538 Drag::start_grab (event, cursor);
2539 show_verbose_cursor_time (adjusted_current_frame(event));
2543 MeterMarkerDrag::setup_pointer_frame_offset ()
2545 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2549 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2551 if (!_marker->meter().movable()) {
2557 // create a dummy marker for visual representation of moving the
2558 // section, because whether its a copy or not, we're going to
2559 // leave or lose the original marker (leave if its a copy; lose if its
2560 // not, because we'll remove it from the map).
2562 MeterSection section (_marker->meter());
2564 if (!section.movable()) {
2569 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2571 _marker = new MeterMarker (
2573 *_editor->meter_group,
2574 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2576 *new MeterSection (_marker->meter())
2579 /* use the new marker for the grab */
2580 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2583 TempoMap& map (_editor->session()->tempo_map());
2584 /* get current state */
2585 before_state = &map.get_state();
2586 /* remove the section while we drag it */
2587 map.remove_meter (section, true);
2591 framepos_t const pf = adjusted_current_frame (event);
2592 _marker->set_position (pf);
2593 show_verbose_cursor_time (pf);
2597 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2599 if (!movement_occurred) {
2600 if (was_double_click()) {
2601 _editor->edit_meter_marker (*_marker);
2606 if (!_marker->meter().movable()) {
2610 motion (event, false);
2612 Timecode::BBT_Time when;
2614 TempoMap& map (_editor->session()->tempo_map());
2615 map.bbt_time (last_pointer_frame(), when);
2617 if (_copy == true) {
2618 _editor->begin_reversible_command (_("copy meter mark"));
2619 XMLNode &before = map.get_state();
2620 map.add_meter (_marker->meter(), when);
2621 XMLNode &after = map.get_state();
2622 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2623 _editor->commit_reversible_command ();
2626 _editor->begin_reversible_command (_("move meter mark"));
2628 /* we removed it before, so add it back now */
2630 map.add_meter (_marker->meter(), when);
2631 XMLNode &after = map.get_state();
2632 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2633 _editor->commit_reversible_command ();
2636 // delete the dummy marker we used for visual representation while moving.
2637 // a new visual marker will show up automatically.
2642 MeterMarkerDrag::aborted (bool moved)
2644 _marker->set_position (_marker->meter().frame ());
2647 TempoMap& map (_editor->session()->tempo_map());
2648 /* we removed it before, so add it back now */
2649 map.add_meter (_marker->meter(), _marker->meter().frame());
2650 // delete the dummy marker we used for visual representation while moving.
2651 // a new visual marker will show up automatically.
2656 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2660 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2662 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2667 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2669 Drag::start_grab (event, cursor);
2670 show_verbose_cursor_time (adjusted_current_frame (event));
2674 TempoMarkerDrag::setup_pointer_frame_offset ()
2676 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2680 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2682 if (!_marker->tempo().movable()) {
2688 // create a dummy marker for visual representation of moving the
2689 // section, because whether its a copy or not, we're going to
2690 // leave or lose the original marker (leave if its a copy; lose if its
2691 // not, because we'll remove it from the map).
2693 // create a dummy marker for visual representation of moving the copy.
2694 // The actual copying is not done before we reach the finish callback.
2697 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2699 TempoSection section (_marker->tempo());
2701 _marker = new TempoMarker (
2703 *_editor->tempo_group,
2704 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2706 *new TempoSection (_marker->tempo())
2709 /* use the new marker for the grab */
2710 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2713 TempoMap& map (_editor->session()->tempo_map());
2714 /* get current state */
2715 before_state = &map.get_state();
2716 /* remove the section while we drag it */
2717 map.remove_tempo (section, true);
2721 framepos_t const pf = adjusted_current_frame (event);
2722 _marker->set_position (pf);
2723 show_verbose_cursor_time (pf);
2727 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2729 if (!movement_occurred) {
2730 if (was_double_click()) {
2731 _editor->edit_tempo_marker (*_marker);
2736 if (!_marker->tempo().movable()) {
2740 motion (event, false);
2742 TempoMap& map (_editor->session()->tempo_map());
2743 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2744 Timecode::BBT_Time when;
2746 map.bbt_time (beat_time, when);
2748 if (_copy == true) {
2749 _editor->begin_reversible_command (_("copy tempo mark"));
2750 XMLNode &before = map.get_state();
2751 map.add_tempo (_marker->tempo(), when);
2752 XMLNode &after = map.get_state();
2753 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2754 _editor->commit_reversible_command ();
2757 _editor->begin_reversible_command (_("move tempo mark"));
2758 /* we removed it before, so add it back now */
2759 map.add_tempo (_marker->tempo(), when);
2760 XMLNode &after = map.get_state();
2761 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2762 _editor->commit_reversible_command ();
2765 // delete the dummy marker we used for visual representation while moving.
2766 // a new visual marker will show up automatically.
2771 TempoMarkerDrag::aborted (bool moved)
2773 _marker->set_position (_marker->tempo().frame());
2775 TempoMap& map (_editor->session()->tempo_map());
2776 /* we removed it before, so add it back now */
2777 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2778 // delete the dummy marker we used for visual representation while moving.
2779 // a new visual marker will show up automatically.
2784 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2785 : Drag (e, &c.track_canvas_item(), false)
2789 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2792 /** Do all the things we do when dragging the playhead to make it look as though
2793 * we have located, without actually doing the locate (because that would cause
2794 * the diskstream buffers to be refilled, which is too slow).
2797 CursorDrag::fake_locate (framepos_t t)
2799 _editor->playhead_cursor->set_position (t);
2801 Session* s = _editor->session ();
2802 if (s->timecode_transmission_suspended ()) {
2803 framepos_t const f = _editor->playhead_cursor->current_frame ();
2804 /* This is asynchronous so it will be sent "now"
2806 s->send_mmc_locate (f);
2807 /* These are synchronous and will be sent during the next
2810 s->queue_full_time_code ();
2811 s->queue_song_position_pointer ();
2814 show_verbose_cursor_time (t);
2815 _editor->UpdateAllTransportClocks (t);
2819 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2821 Drag::start_grab (event, c);
2823 _grab_zoom = _editor->samples_per_pixel;
2825 framepos_t where = _editor->canvas_event_sample (event);
2827 _editor->snap_to_with_modifier (where, event);
2829 _editor->_dragging_playhead = true;
2831 Session* s = _editor->session ();
2833 /* grab the track canvas item as well */
2835 _cursor.track_canvas_item().grab();
2838 if (_was_rolling && _stop) {
2842 if (s->is_auditioning()) {
2843 s->cancel_audition ();
2847 if (AudioEngine::instance()->connected()) {
2849 /* do this only if we're the engine is connected
2850 * because otherwise this request will never be
2851 * serviced and we'll busy wait forever. likewise,
2852 * notice if we are disconnected while waiting for the
2853 * request to be serviced.
2856 s->request_suspend_timecode_transmission ();
2857 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2858 /* twiddle our thumbs */
2863 fake_locate (where);
2867 CursorDrag::motion (GdkEvent* event, bool)
2869 framepos_t const adjusted_frame = adjusted_current_frame (event);
2870 if (adjusted_frame != last_pointer_frame()) {
2871 fake_locate (adjusted_frame);
2876 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2878 _editor->_dragging_playhead = false;
2880 _cursor.track_canvas_item().ungrab();
2882 if (!movement_occurred && _stop) {
2886 motion (event, false);
2888 Session* s = _editor->session ();
2890 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2891 _editor->_pending_locate_request = true;
2892 s->request_resume_timecode_transmission ();
2897 CursorDrag::aborted (bool)
2899 _cursor.track_canvas_item().ungrab();
2901 if (_editor->_dragging_playhead) {
2902 _editor->session()->request_resume_timecode_transmission ();
2903 _editor->_dragging_playhead = false;
2906 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2909 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2910 : RegionDrag (e, i, p, v)
2912 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2916 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2918 Drag::start_grab (event, cursor);
2920 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2921 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2923 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2927 FadeInDrag::setup_pointer_frame_offset ()
2929 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2930 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2931 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2935 FadeInDrag::motion (GdkEvent* event, bool)
2937 framecnt_t fade_length;
2939 framepos_t const pos = adjusted_current_frame (event);
2941 boost::shared_ptr<Region> region = _primary->region ();
2943 if (pos < (region->position() + 64)) {
2944 fade_length = 64; // this should be a minimum defined somewhere
2945 } else if (pos > region->last_frame()) {
2946 fade_length = region->length();
2948 fade_length = pos - region->position();
2951 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2953 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2959 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2962 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2966 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2968 if (!movement_occurred) {
2972 framecnt_t fade_length;
2974 framepos_t const pos = adjusted_current_frame (event);
2976 boost::shared_ptr<Region> region = _primary->region ();
2978 if (pos < (region->position() + 64)) {
2979 fade_length = 64; // this should be a minimum defined somewhere
2980 } else if (pos > region->last_frame()) {
2981 fade_length = region->length();
2983 fade_length = pos - region->position();
2986 _editor->begin_reversible_command (_("change fade in length"));
2988 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2990 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2996 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2997 XMLNode &before = alist->get_state();
2999 tmp->audio_region()->set_fade_in_length (fade_length);
3000 tmp->audio_region()->set_fade_in_active (true);
3002 XMLNode &after = alist->get_state();
3003 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3006 _editor->commit_reversible_command ();
3010 FadeInDrag::aborted (bool)
3012 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3013 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3019 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3023 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3024 : RegionDrag (e, i, p, v)
3026 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3030 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3032 Drag::start_grab (event, cursor);
3034 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3035 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3037 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3041 FadeOutDrag::setup_pointer_frame_offset ()
3043 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3044 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3045 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3049 FadeOutDrag::motion (GdkEvent* event, bool)
3051 framecnt_t fade_length;
3053 framepos_t const pos = adjusted_current_frame (event);
3055 boost::shared_ptr<Region> region = _primary->region ();
3057 if (pos > (region->last_frame() - 64)) {
3058 fade_length = 64; // this should really be a minimum fade defined somewhere
3060 else if (pos < region->position()) {
3061 fade_length = region->length();
3064 fade_length = region->last_frame() - pos;
3067 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3069 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3075 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3078 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3082 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3084 if (!movement_occurred) {
3088 framecnt_t fade_length;
3090 framepos_t const pos = adjusted_current_frame (event);
3092 boost::shared_ptr<Region> region = _primary->region ();
3094 if (pos > (region->last_frame() - 64)) {
3095 fade_length = 64; // this should really be a minimum fade defined somewhere
3097 else if (pos < region->position()) {
3098 fade_length = region->length();
3101 fade_length = region->last_frame() - pos;
3104 _editor->begin_reversible_command (_("change fade out length"));
3106 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3108 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3114 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3115 XMLNode &before = alist->get_state();
3117 tmp->audio_region()->set_fade_out_length (fade_length);
3118 tmp->audio_region()->set_fade_out_active (true);
3120 XMLNode &after = alist->get_state();
3121 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3124 _editor->commit_reversible_command ();
3128 FadeOutDrag::aborted (bool)
3130 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3131 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3137 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3141 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3144 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3146 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3149 _points.push_back (ArdourCanvas::Duple (0, 0));
3150 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3153 MarkerDrag::~MarkerDrag ()
3155 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3160 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3162 location = new Location (*l);
3163 markers.push_back (m);
3168 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3170 Drag::start_grab (event, cursor);
3174 Location *location = _editor->find_location_from_marker (_marker, is_start);
3175 _editor->_dragging_edit_point = true;
3177 update_item (location);
3179 // _drag_line->show();
3180 // _line->raise_to_top();
3183 show_verbose_cursor_time (location->start());
3185 show_verbose_cursor_time (location->end());
3188 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3191 case Selection::Toggle:
3192 /* we toggle on the button release */
3194 case Selection::Set:
3195 if (!_editor->selection->selected (_marker)) {
3196 _editor->selection->set (_marker);
3199 case Selection::Extend:
3201 Locations::LocationList ll;
3202 list<Marker*> to_add;
3204 _editor->selection->markers.range (s, e);
3205 s = min (_marker->position(), s);
3206 e = max (_marker->position(), e);
3209 if (e < max_framepos) {
3212 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3213 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3214 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3217 to_add.push_back (lm->start);
3220 to_add.push_back (lm->end);
3224 if (!to_add.empty()) {
3225 _editor->selection->add (to_add);
3229 case Selection::Add:
3230 _editor->selection->add (_marker);
3234 /* Set up copies for us to manipulate during the drag
3237 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3239 Location* l = _editor->find_location_from_marker (*i, is_start);
3246 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3248 /* range: check that the other end of the range isn't
3251 CopiedLocationInfo::iterator x;
3252 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3253 if (*(*x).location == *l) {
3257 if (x == _copied_locations.end()) {
3258 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3260 (*x).markers.push_back (*i);
3261 (*x).move_both = true;
3269 MarkerDrag::setup_pointer_frame_offset ()
3272 Location *location = _editor->find_location_from_marker (_marker, is_start);
3273 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3277 MarkerDrag::motion (GdkEvent* event, bool)
3279 framecnt_t f_delta = 0;
3281 bool move_both = false;
3282 Location *real_location;
3283 Location *copy_location = 0;
3285 framepos_t const newframe = adjusted_current_frame (event);
3286 framepos_t next = newframe;
3288 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3292 CopiedLocationInfo::iterator x;
3294 /* find the marker we're dragging, and compute the delta */
3296 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3298 copy_location = (*x).location;
3300 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3302 /* this marker is represented by this
3303 * CopiedLocationMarkerInfo
3306 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3311 if (real_location->is_mark()) {
3312 f_delta = newframe - copy_location->start();
3316 switch (_marker->type()) {
3317 case Marker::SessionStart:
3318 case Marker::RangeStart:
3319 case Marker::LoopStart:
3320 case Marker::PunchIn:
3321 f_delta = newframe - copy_location->start();
3324 case Marker::SessionEnd:
3325 case Marker::RangeEnd:
3326 case Marker::LoopEnd:
3327 case Marker::PunchOut:
3328 f_delta = newframe - copy_location->end();
3331 /* what kind of marker is this ? */
3340 if (x == _copied_locations.end()) {
3341 /* hmm, impossible - we didn't find the dragged marker */
3345 /* now move them all */
3347 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3349 copy_location = x->location;
3351 /* call this to find out if its the start or end */
3353 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3357 if (real_location->locked()) {
3361 if (copy_location->is_mark()) {
3365 copy_location->set_start (copy_location->start() + f_delta);
3369 framepos_t new_start = copy_location->start() + f_delta;
3370 framepos_t new_end = copy_location->end() + f_delta;
3372 if (is_start) { // start-of-range marker
3374 if (move_both || (*x).move_both) {
3375 copy_location->set_start (new_start);
3376 copy_location->set_end (new_end);
3377 } else if (new_start < copy_location->end()) {
3378 copy_location->set_start (new_start);
3379 } else if (newframe > 0) {
3380 _editor->snap_to (next, 1, true);
3381 copy_location->set_end (next);
3382 copy_location->set_start (newframe);
3385 } else { // end marker
3387 if (move_both || (*x).move_both) {
3388 copy_location->set_end (new_end);
3389 copy_location->set_start (new_start);
3390 } else if (new_end > copy_location->start()) {
3391 copy_location->set_end (new_end);
3392 } else if (newframe > 0) {
3393 _editor->snap_to (next, -1, true);
3394 copy_location->set_start (next);
3395 copy_location->set_end (newframe);
3400 update_item (copy_location);
3402 /* now lookup the actual GUI items used to display this
3403 * location and move them to wherever the copy of the location
3404 * is now. This means that the logic in ARDOUR::Location is
3405 * still enforced, even though we are not (yet) modifying
3406 * the real Location itself.
3409 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3412 lm->set_position (copy_location->start(), copy_location->end());
3417 assert (!_copied_locations.empty());
3419 show_verbose_cursor_time (newframe);
3423 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3425 if (!movement_occurred) {
3427 if (was_double_click()) {
3428 _editor->rename_marker (_marker);
3432 /* just a click, do nothing but finish
3433 off the selection process
3436 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3439 case Selection::Set:
3440 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3441 _editor->selection->set (_marker);
3445 case Selection::Toggle:
3446 /* we toggle on the button release, click only */
3447 _editor->selection->toggle (_marker);
3450 case Selection::Extend:
3451 case Selection::Add:
3458 _editor->_dragging_edit_point = false;
3460 _editor->begin_reversible_command ( _("move marker") );
3461 XMLNode &before = _editor->session()->locations()->get_state();
3463 MarkerSelection::iterator i;
3464 CopiedLocationInfo::iterator x;
3467 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3468 x != _copied_locations.end() && i != _editor->selection->markers.end();
3471 Location * location = _editor->find_location_from_marker (*i, is_start);
3475 if (location->locked()) {
3479 if (location->is_mark()) {
3480 location->set_start (((*x).location)->start());
3482 location->set (((*x).location)->start(), ((*x).location)->end());
3487 XMLNode &after = _editor->session()->locations()->get_state();
3488 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3489 _editor->commit_reversible_command ();
3493 MarkerDrag::aborted (bool)
3499 MarkerDrag::update_item (Location*)
3504 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3506 _cumulative_x_drag (0),
3507 _cumulative_y_drag (0)
3509 if (_zero_gain_fraction < 0.0) {
3510 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3513 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3515 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3521 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3523 Drag::start_grab (event, _editor->cursors()->fader);
3525 // start the grab at the center of the control point so
3526 // the point doesn't 'jump' to the mouse after the first drag
3527 _fixed_grab_x = _point->get_x();
3528 _fixed_grab_y = _point->get_y();
3530 float const fraction = 1 - (_point->get_y() / _point->line().height());
3532 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3534 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3536 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3538 if (!_point->can_slide ()) {
3539 _x_constrained = true;
3544 ControlPointDrag::motion (GdkEvent* event, bool)
3546 double dx = _drags->current_pointer_x() - last_pointer_x();
3547 double dy = current_pointer_y() - last_pointer_y();
3549 if (event->button.state & Keyboard::SecondaryModifier) {
3554 /* coordinate in pixels relative to the start of the region (for region-based automation)
3555 or track (for track-based automation) */
3556 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3557 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3559 // calculate zero crossing point. back off by .01 to stay on the
3560 // positive side of zero
3561 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3563 // make sure we hit zero when passing through
3564 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3568 if (_x_constrained) {
3571 if (_y_constrained) {
3575 _cumulative_x_drag = cx - _fixed_grab_x;
3576 _cumulative_y_drag = cy - _fixed_grab_y;
3580 cy = min ((double) _point->line().height(), cy);
3582 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3584 if (!_x_constrained) {
3585 _editor->snap_to_with_modifier (cx_frames, event);
3588 cx_frames = min (cx_frames, _point->line().maximum_time());
3590 float const fraction = 1.0 - (cy / _point->line().height());
3592 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3594 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3598 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3600 if (!movement_occurred) {
3604 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3605 _editor->reset_point_selection ();
3609 motion (event, false);
3612 _point->line().end_drag (_pushing, _final_index);
3613 _editor->session()->commit_reversible_command ();
3617 ControlPointDrag::aborted (bool)
3619 _point->line().reset ();
3623 ControlPointDrag::active (Editing::MouseMode m)
3625 if (m == Editing::MouseGain) {
3626 /* always active in mouse gain */
3630 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3631 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3634 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3637 _cumulative_y_drag (0)
3639 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3643 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3645 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3648 _item = &_line->grab_item ();
3650 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3651 origin, and ditto for y.
3654 double cx = event->button.x;
3655 double cy = event->button.y;
3657 _line->parent_group().canvas_to_item (cx, cy);
3659 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3664 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3665 /* no adjacent points */
3669 Drag::start_grab (event, _editor->cursors()->fader);
3671 /* store grab start in parent frame */
3676 double fraction = 1.0 - (cy / _line->height());
3678 _line->start_drag_line (before, after, fraction);
3680 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3684 LineDrag::motion (GdkEvent* event, bool)
3686 double dy = current_pointer_y() - last_pointer_y();
3688 if (event->button.state & Keyboard::SecondaryModifier) {
3692 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3694 _cumulative_y_drag = cy - _fixed_grab_y;
3697 cy = min ((double) _line->height(), cy);
3699 double const fraction = 1.0 - (cy / _line->height());
3702 /* we are ignoring x position for this drag, so we can just pass in anything */
3703 _line->drag_motion (0, fraction, true, false, ignored);
3705 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3709 LineDrag::finished (GdkEvent* event, bool movement_occured)
3711 if (movement_occured) {
3712 motion (event, false);
3713 _line->end_drag (false, 0);
3715 /* add a new control point on the line */
3717 AutomationTimeAxisView* atv;
3719 _line->end_drag (false, 0);
3721 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3722 framepos_t where = _editor->window_event_sample (event, 0, 0);
3723 atv->add_automation_event (event, where, event->button.y, false);
3727 _editor->session()->commit_reversible_command ();
3731 LineDrag::aborted (bool)
3736 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3739 _cumulative_x_drag (0)
3741 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3745 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3747 Drag::start_grab (event);
3749 _line = reinterpret_cast<Line*> (_item);
3752 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3754 double cx = event->button.x;
3755 double cy = event->button.y;
3757 _item->parent()->canvas_to_item (cx, cy);
3759 /* store grab start in parent frame */
3760 _region_view_grab_x = cx;
3762 _before = *(float*) _item->get_data ("position");
3764 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3766 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3770 FeatureLineDrag::motion (GdkEvent*, bool)
3772 double dx = _drags->current_pointer_x() - last_pointer_x();
3774 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3776 _cumulative_x_drag += dx;
3778 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3787 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3789 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3791 float *pos = new float;
3794 _line->set_data ("position", pos);
3800 FeatureLineDrag::finished (GdkEvent*, bool)
3802 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3803 _arv->update_transient(_before, _before);
3807 FeatureLineDrag::aborted (bool)
3812 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3814 , _vertical_only (false)
3816 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3820 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3822 Drag::start_grab (event);
3823 show_verbose_cursor_time (adjusted_current_frame (event));
3827 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3834 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3836 framepos_t grab = grab_frame ();
3837 if (Config->get_rubberbanding_snaps_to_grid ()) {
3838 _editor->snap_to_with_modifier (grab, event);
3841 /* base start and end on initial click position */
3851 if (current_pointer_y() < grab_y()) {
3852 y1 = current_pointer_y();
3855 y2 = current_pointer_y();
3859 if (start != end || y1 != y2) {
3861 double x1 = _editor->sample_to_pixel (start);
3862 double x2 = _editor->sample_to_pixel (end);
3863 const double min_dimension = 2.0;
3865 if (_vertical_only) {
3866 /* fixed 10 pixel width */
3870 x2 = min (x1 - min_dimension, x2);
3872 x2 = max (x1 + min_dimension, x2);
3877 y2 = min (y1 - min_dimension, y2);
3879 y2 = max (y1 + min_dimension, y2);
3882 /* translate rect into item space and set */
3884 ArdourCanvas::Rect r (x1, y1, x2, y2);
3886 /* this drag is a _trackview_only == true drag, so the y1 and
3887 * y2 (computed using current_pointer_y() and grab_y()) will be
3888 * relative to the top of the trackview group). The
3889 * rubberband rect has the same parent/scroll offset as the
3890 * the trackview group, so we can use the "r" rect directly
3891 * to set the shape of the rubberband.
3894 _editor->rubberband_rect->set (r);
3895 _editor->rubberband_rect->show();
3896 _editor->rubberband_rect->raise_to_top();
3898 show_verbose_cursor_time (pf);
3900 do_select_things (event, true);
3905 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3910 if (grab_frame() < last_pointer_frame()) {
3912 x2 = last_pointer_frame ();
3915 x1 = last_pointer_frame ();
3921 if (current_pointer_y() < grab_y()) {
3922 y1 = current_pointer_y();
3925 y2 = current_pointer_y();
3929 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3933 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3935 if (movement_occurred) {
3937 motion (event, false);
3938 do_select_things (event, false);
3944 bool do_deselect = true;
3945 MidiTimeAxisView* mtv;
3947 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3949 if (_editor->selection->empty()) {
3950 /* nothing selected */
3951 add_midi_region (mtv);
3952 do_deselect = false;
3956 /* do not deselect if Primary or Tertiary (toggle-select or
3957 * extend-select are pressed.
3960 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3961 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3968 _editor->rubberband_rect->hide();
3972 RubberbandSelectDrag::aborted (bool)
3974 _editor->rubberband_rect->hide ();
3977 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3978 : RegionDrag (e, i, p, v)
3980 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3984 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3986 Drag::start_grab (event, cursor);
3988 show_verbose_cursor_time (adjusted_current_frame (event));
3992 TimeFXDrag::motion (GdkEvent* event, bool)
3994 RegionView* rv = _primary;
3995 StreamView* cv = rv->get_time_axis_view().view ();
3997 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3998 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3999 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4001 framepos_t const pf = adjusted_current_frame (event);
4003 if (pf > rv->region()->position()) {
4004 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4007 show_verbose_cursor_time (pf);
4011 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4013 _primary->get_time_axis_view().hide_timestretch ();
4015 if (!movement_occurred) {
4019 if (last_pointer_frame() < _primary->region()->position()) {
4020 /* backwards drag of the left edge - not usable */
4024 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4026 float percentage = (double) newlen / (double) _primary->region()->length();
4028 #ifndef USE_RUBBERBAND
4029 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4030 if (_primary->region()->data_type() == DataType::AUDIO) {
4031 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4035 if (!_editor->get_selection().regions.empty()) {
4036 /* primary will already be included in the selection, and edit
4037 group shared editing will propagate selection across
4038 equivalent regions, so just use the current region
4042 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4043 error << _("An error occurred while executing time stretch operation") << endmsg;
4049 TimeFXDrag::aborted (bool)
4051 _primary->get_time_axis_view().hide_timestretch ();
4054 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4057 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4061 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4063 Drag::start_grab (event);
4067 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4069 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4073 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4075 if (movement_occurred && _editor->session()) {
4076 /* make sure we stop */
4077 _editor->session()->request_transport_speed (0.0);
4082 ScrubDrag::aborted (bool)
4087 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4092 , _original_pointer_time_axis (-1)
4093 , _last_pointer_time_axis (-1)
4094 , _time_selection_at_start (!_editor->get_selection().time.empty())
4096 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4098 if (_time_selection_at_start) {
4099 start_at_start = _editor->get_selection().time.start();
4100 end_at_start = _editor->get_selection().time.end_frame();
4105 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4107 if (_editor->session() == 0) {
4111 Gdk::Cursor* cursor = 0;
4113 switch (_operation) {
4114 case CreateSelection:
4115 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4120 cursor = _editor->cursors()->selector;
4121 Drag::start_grab (event, cursor);
4124 case SelectionStartTrim:
4125 if (_editor->clicked_axisview) {
4126 _editor->clicked_axisview->order_selection_trims (_item, true);
4128 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4131 case SelectionEndTrim:
4132 if (_editor->clicked_axisview) {
4133 _editor->clicked_axisview->order_selection_trims (_item, false);
4135 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4139 Drag::start_grab (event, cursor);
4142 case SelectionExtend:
4143 Drag::start_grab (event, cursor);
4147 if (_operation == SelectionMove) {
4148 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4150 show_verbose_cursor_time (adjusted_current_frame (event));
4153 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4157 SelectionDrag::setup_pointer_frame_offset ()
4159 switch (_operation) {
4160 case CreateSelection:
4161 _pointer_frame_offset = 0;
4164 case SelectionStartTrim:
4166 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4169 case SelectionEndTrim:
4170 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4173 case SelectionExtend:
4179 SelectionDrag::motion (GdkEvent* event, bool first_move)
4181 framepos_t start = 0;
4183 framecnt_t length = 0;
4184 framecnt_t distance = 0;
4186 framepos_t const pending_position = adjusted_current_frame (event);
4188 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4192 switch (_operation) {
4193 case CreateSelection:
4195 framepos_t grab = grab_frame ();
4198 grab = adjusted_current_frame (event, false);
4199 if (grab < pending_position) {
4200 _editor->snap_to (grab, -1);
4202 _editor->snap_to (grab, 1);
4206 if (pending_position < grab) {
4207 start = pending_position;
4210 end = pending_position;
4214 /* first drag: Either add to the selection
4215 or create a new selection
4222 /* adding to the selection */
4223 _editor->set_selected_track_as_side_effect (Selection::Add);
4224 _editor->clicked_selection = _editor->selection->add (start, end);
4231 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4232 _editor->set_selected_track_as_side_effect (Selection::Set);
4235 _editor->clicked_selection = _editor->selection->set (start, end);
4239 /* select all tracks within the rectangle that we've marked out so far */
4240 TrackViewList to_be_added_to_selection;
4241 TrackViewList to_be_removed_from_selection;
4242 TrackViewList& all_tracks (_editor->track_views);
4244 ArdourCanvas::Coord const top = grab_y();
4245 ArdourCanvas::Coord const bottom = current_pointer_y();
4247 if (top >= 0 && bottom >= 0) {
4249 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4251 if ((*i)->covered_by_y_range (top, bottom)) {
4252 if (!(*i)->get_selected()) {
4253 to_be_added_to_selection.push_back (*i);
4256 if ((*i)->get_selected()) {
4257 to_be_removed_from_selection.push_back (*i);
4262 if (!to_be_added_to_selection.empty()) {
4263 _editor->selection->add (to_be_added_to_selection);
4266 if (!to_be_removed_from_selection.empty()) {
4267 _editor->selection->remove (to_be_removed_from_selection);
4273 case SelectionStartTrim:
4275 start = _editor->selection->time[_editor->clicked_selection].start;
4276 end = _editor->selection->time[_editor->clicked_selection].end;
4278 if (pending_position > end) {
4281 start = pending_position;
4285 case SelectionEndTrim:
4287 start = _editor->selection->time[_editor->clicked_selection].start;
4288 end = _editor->selection->time[_editor->clicked_selection].end;
4290 if (pending_position < start) {
4293 end = pending_position;
4300 start = _editor->selection->time[_editor->clicked_selection].start;
4301 end = _editor->selection->time[_editor->clicked_selection].end;
4303 length = end - start;
4304 distance = pending_position - start;
4305 start = pending_position;
4306 _editor->snap_to (start);
4308 end = start + length;
4312 case SelectionExtend:
4317 switch (_operation) {
4319 if (_time_selection_at_start) {
4320 _editor->selection->move_time (distance);
4324 _editor->selection->replace (_editor->clicked_selection, start, end);
4328 if (_operation == SelectionMove) {
4329 show_verbose_cursor_time(start);
4331 show_verbose_cursor_time(pending_position);
4336 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4338 Session* s = _editor->session();
4340 if (movement_occurred) {
4341 motion (event, false);
4342 /* XXX this is not object-oriented programming at all. ick */
4343 if (_editor->selection->time.consolidate()) {
4344 _editor->selection->TimeChanged ();
4347 /* XXX what if its a music time selection? */
4349 if ( s->get_play_range() && s->transport_rolling() ) {
4350 s->request_play_range (&_editor->selection->time, true);
4352 if (Config->get_follow_edits() && !s->transport_rolling()) {
4353 s->request_locate (_editor->get_selection().time.start());
4359 /* just a click, no pointer movement.
4362 if (_operation == SelectionExtend) {
4363 if (_time_selection_at_start) {
4364 framepos_t pos = adjusted_current_frame (event, false);
4365 framepos_t start = min (pos, start_at_start);
4366 framepos_t end = max (pos, end_at_start);
4367 _editor->selection->set (start, end);
4370 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4371 if (_editor->clicked_selection) {
4372 _editor->selection->remove (_editor->clicked_selection);
4375 if (!_editor->clicked_selection) {
4376 _editor->selection->clear_time();
4381 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4382 _editor->selection->set (_editor->clicked_axisview);
4385 if (s && s->get_play_range () && s->transport_rolling()) {
4386 s->request_stop (false, false);
4391 _editor->stop_canvas_autoscroll ();
4392 _editor->clicked_selection = 0;
4396 SelectionDrag::aborted (bool)
4401 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4402 : Drag (e, i, false),
4406 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4408 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4409 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4410 physical_screen_height (_editor->get_window())));
4411 _drag_rect->hide ();
4413 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4414 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4418 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4420 if (_editor->session() == 0) {
4424 Gdk::Cursor* cursor = 0;
4426 if (!_editor->temp_location) {
4427 _editor->temp_location = new Location (*_editor->session());
4430 switch (_operation) {
4431 case CreateRangeMarker:
4432 case CreateTransportMarker:
4433 case CreateCDMarker:
4435 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4440 cursor = _editor->cursors()->selector;
4444 Drag::start_grab (event, cursor);
4446 show_verbose_cursor_time (adjusted_current_frame (event));
4450 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4452 framepos_t start = 0;
4454 ArdourCanvas::Rectangle *crect;
4456 switch (_operation) {
4457 case CreateRangeMarker:
4458 crect = _editor->range_bar_drag_rect;
4460 case CreateTransportMarker:
4461 crect = _editor->transport_bar_drag_rect;
4463 case CreateCDMarker:
4464 crect = _editor->cd_marker_bar_drag_rect;
4467 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4472 framepos_t const pf = adjusted_current_frame (event);
4474 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4475 framepos_t grab = grab_frame ();
4476 _editor->snap_to (grab);
4478 if (pf < grab_frame()) {
4486 /* first drag: Either add to the selection
4487 or create a new selection.
4492 _editor->temp_location->set (start, end);
4496 update_item (_editor->temp_location);
4498 //_drag_rect->raise_to_top();
4504 _editor->temp_location->set (start, end);
4506 double x1 = _editor->sample_to_pixel (start);
4507 double x2 = _editor->sample_to_pixel (end);
4511 update_item (_editor->temp_location);
4514 show_verbose_cursor_time (pf);
4519 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4521 Location * newloc = 0;
4525 if (movement_occurred) {
4526 motion (event, false);
4529 switch (_operation) {
4530 case CreateRangeMarker:
4531 case CreateCDMarker:
4533 _editor->begin_reversible_command (_("new range marker"));
4534 XMLNode &before = _editor->session()->locations()->get_state();
4535 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4536 if (_operation == CreateCDMarker) {
4537 flags = Location::IsRangeMarker | Location::IsCDMarker;
4538 _editor->cd_marker_bar_drag_rect->hide();
4541 flags = Location::IsRangeMarker;
4542 _editor->range_bar_drag_rect->hide();
4544 newloc = new Location (
4545 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4548 _editor->session()->locations()->add (newloc, true);
4549 XMLNode &after = _editor->session()->locations()->get_state();
4550 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4551 _editor->commit_reversible_command ();
4555 case CreateTransportMarker:
4556 // popup menu to pick loop or punch
4557 _editor->new_transport_marker_context_menu (&event->button, _item);
4563 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4565 if (_operation == CreateTransportMarker) {
4567 /* didn't drag, so just locate */
4569 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4571 } else if (_operation == CreateCDMarker) {
4573 /* didn't drag, but mark is already created so do
4576 } else { /* operation == CreateRangeMarker */
4582 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4584 if (end == max_framepos) {
4585 end = _editor->session()->current_end_frame ();
4588 if (start == max_framepos) {
4589 start = _editor->session()->current_start_frame ();
4592 switch (_editor->mouse_mode) {
4594 /* find the two markers on either side and then make the selection from it */
4595 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4599 /* find the two markers on either side of the click and make the range out of it */
4600 _editor->selection->set (start, end);
4609 _editor->stop_canvas_autoscroll ();
4613 RangeMarkerBarDrag::aborted (bool)
4619 RangeMarkerBarDrag::update_item (Location* location)
4621 double const x1 = _editor->sample_to_pixel (location->start());
4622 double const x2 = _editor->sample_to_pixel (location->end());
4624 _drag_rect->set_x0 (x1);
4625 _drag_rect->set_x1 (x2);
4628 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4632 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4636 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4638 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4639 Drag::start_grab (event, _editor->cursors()->zoom_out);
4642 Drag::start_grab (event, _editor->cursors()->zoom_in);
4646 show_verbose_cursor_time (adjusted_current_frame (event));
4650 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4655 framepos_t const pf = adjusted_current_frame (event);
4657 framepos_t grab = grab_frame ();
4658 _editor->snap_to_with_modifier (grab, event);
4660 /* base start and end on initial click position */
4672 _editor->zoom_rect->show();
4673 _editor->zoom_rect->raise_to_top();
4676 _editor->reposition_zoom_rect(start, end);
4678 show_verbose_cursor_time (pf);
4683 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4685 if (movement_occurred) {
4686 motion (event, false);
4688 if (grab_frame() < last_pointer_frame()) {
4689 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4691 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4694 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4695 _editor->tav_zoom_step (_zoom_out);
4697 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4701 _editor->zoom_rect->hide();
4705 MouseZoomDrag::aborted (bool)
4707 _editor->zoom_rect->hide ();
4710 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4712 , _cumulative_dx (0)
4713 , _cumulative_dy (0)
4715 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4717 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4719 _region = &_primary->region_view ();
4720 _note_height = _region->midi_stream_view()->note_height ();
4724 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4726 Drag::start_grab (event);
4728 if (!(_was_selected = _primary->selected())) {
4730 /* tertiary-click means extend selection - we'll do that on button release,
4731 so don't add it here, because otherwise we make it hard to figure
4732 out the "extend-to" range.
4735 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4738 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4741 _region->note_selected (_primary, true);
4743 _region->unique_select (_primary);
4749 /** @return Current total drag x change in frames */
4751 NoteDrag::total_dx () const
4754 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4756 /* primary note time */
4757 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4759 /* new time of the primary note in session frames */
4760 frameoffset_t st = n + dx;
4762 framepos_t const rp = _region->region()->position ();
4764 /* prevent the note being dragged earlier than the region's position */
4767 /* snap and return corresponding delta */
4768 return _region->snap_frame_to_frame (st - rp) + rp - n;
4771 /** @return Current total drag y change in note number */
4773 NoteDrag::total_dy () const
4775 MidiStreamView* msv = _region->midi_stream_view ();
4776 double const y = _region->midi_view()->y_position ();
4777 /* new current note */
4778 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4780 n = max (msv->lowest_note(), n);
4781 n = min (msv->highest_note(), n);
4782 /* and work out delta */
4783 return n - msv->y_to_note (grab_y() - y);
4787 NoteDrag::motion (GdkEvent *, bool)
4789 /* Total change in x and y since the start of the drag */
4790 frameoffset_t const dx = total_dx ();
4791 int8_t const dy = total_dy ();
4793 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4794 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4795 double const tdy = -dy * _note_height - _cumulative_dy;
4798 _cumulative_dx += tdx;
4799 _cumulative_dy += tdy;
4801 int8_t note_delta = total_dy();
4803 _region->move_selection (tdx, tdy, note_delta);
4805 /* the new note value may be the same as the old one, but we
4806 * don't know what that means because the selection may have
4807 * involved more than one note and we might be doing something
4808 * odd with them. so show the note value anyway, always.
4812 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4814 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4815 (int) floor ((double)new_note));
4817 show_verbose_cursor_text (buf);
4822 NoteDrag::finished (GdkEvent* ev, bool moved)
4825 /* no motion - select note */
4827 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4828 _editor->current_mouse_mode() == Editing::MouseDraw) {
4830 if (_was_selected) {
4831 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4833 _region->note_deselected (_primary);
4836 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4837 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4839 if (!extend && !add && _region->selection_size() > 1) {
4840 _region->unique_select (_primary);
4841 } else if (extend) {
4842 _region->note_selected (_primary, true, true);
4844 /* it was added during button press */
4849 _region->note_dropped (_primary, total_dx(), total_dy());
4854 NoteDrag::aborted (bool)
4859 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4860 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4861 : Drag (editor, atv->base_item ())
4863 , _nothing_to_drag (false)
4865 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4866 y_origin = atv->y_position();
4867 setup (atv->lines ());
4870 /** Make an AutomationRangeDrag for region gain lines */
4871 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4872 : Drag (editor, rv->get_canvas_group ())
4874 , _nothing_to_drag (false)
4876 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4878 list<boost::shared_ptr<AutomationLine> > lines;
4879 lines.push_back (rv->get_gain_line ());
4880 y_origin = rv->get_time_axis_view().y_position();
4884 /** @param lines AutomationLines to drag.
4885 * @param offset Offset from the session start to the points in the AutomationLines.
4888 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4890 /* find the lines that overlap the ranges being dragged */
4891 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4892 while (i != lines.end ()) {
4893 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4896 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4898 /* check this range against all the AudioRanges that we are using */
4899 list<AudioRange>::const_iterator k = _ranges.begin ();
4900 while (k != _ranges.end()) {
4901 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4907 /* add it to our list if it overlaps at all */
4908 if (k != _ranges.end()) {
4913 _lines.push_back (n);
4919 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4923 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4925 return 1.0 - ((global_y - y_origin) / line->height());
4929 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4931 Drag::start_grab (event, cursor);
4933 /* Get line states before we start changing things */
4934 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4935 i->state = &i->line->get_state ();
4936 i->original_fraction = y_fraction (i->line, current_pointer_y());
4939 if (_ranges.empty()) {
4941 /* No selected time ranges: drag all points */
4942 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4943 uint32_t const N = i->line->npoints ();
4944 for (uint32_t j = 0; j < N; ++j) {
4945 i->points.push_back (i->line->nth (j));
4951 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4953 framecnt_t const half = (i->start + i->end) / 2;
4955 /* find the line that this audio range starts in */
4956 list<Line>::iterator j = _lines.begin();
4957 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4961 if (j != _lines.end()) {
4962 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4964 /* j is the line that this audio range starts in; fade into it;
4965 64 samples length plucked out of thin air.
4968 framepos_t a = i->start + 64;
4973 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4974 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4976 the_list->add (p, the_list->eval (p));
4977 the_list->add (q, the_list->eval (q));
4980 /* same thing for the end */
4983 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4987 if (j != _lines.end()) {
4988 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4990 /* j is the line that this audio range starts in; fade out of it;
4991 64 samples length plucked out of thin air.
4994 framepos_t b = i->end - 64;
4999 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5000 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5002 the_list->add (p, the_list->eval (p));
5003 the_list->add (q, the_list->eval (q));
5007 _nothing_to_drag = true;
5009 /* Find all the points that should be dragged and put them in the relevant
5010 points lists in the Line structs.
5013 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5015 uint32_t const N = i->line->npoints ();
5016 for (uint32_t j = 0; j < N; ++j) {
5018 /* here's a control point on this line */
5019 ControlPoint* p = i->line->nth (j);
5020 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5022 /* see if it's inside a range */
5023 list<AudioRange>::const_iterator k = _ranges.begin ();
5024 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5028 if (k != _ranges.end()) {
5029 /* dragging this point */
5030 _nothing_to_drag = false;
5031 i->points.push_back (p);
5037 if (_nothing_to_drag) {
5041 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5042 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5047 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5049 if (_nothing_to_drag) {
5053 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5054 float const f = y_fraction (l->line, current_pointer_y());
5055 /* we are ignoring x position for this drag, so we can just pass in anything */
5057 l->line->drag_motion (0, f, true, false, ignored);
5058 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5063 AutomationRangeDrag::finished (GdkEvent* event, bool)
5065 if (_nothing_to_drag) {
5069 motion (event, false);
5070 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5071 i->line->end_drag (false, 0);
5074 _editor->session()->commit_reversible_command ();
5078 AutomationRangeDrag::aborted (bool)
5080 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5085 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5087 , initial_time_axis_view (itav)
5089 /* note that time_axis_view may be null if the regionview was created
5090 * as part of a copy operation.
5092 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5093 layer = v->region()->layer ();
5094 initial_y = v->get_canvas_group()->position().y;
5095 initial_playlist = v->region()->playlist ();
5096 initial_position = v->region()->position ();
5097 initial_end = v->region()->position () + v->region()->length ();
5100 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5101 : Drag (e, i->canvas_item ())
5104 , _cumulative_dx (0)
5106 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5107 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5112 PatchChangeDrag::motion (GdkEvent* ev, bool)
5114 framepos_t f = adjusted_current_frame (ev);
5115 boost::shared_ptr<Region> r = _region_view->region ();
5116 f = max (f, r->position ());
5117 f = min (f, r->last_frame ());
5119 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5120 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5121 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5122 _cumulative_dx = dxu;
5126 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5128 if (!movement_occurred) {
5132 boost::shared_ptr<Region> r (_region_view->region ());
5133 framepos_t f = adjusted_current_frame (ev);
5134 f = max (f, r->position ());
5135 f = min (f, r->last_frame ());
5137 _region_view->move_patch_change (
5139 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5144 PatchChangeDrag::aborted (bool)
5146 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5150 PatchChangeDrag::setup_pointer_frame_offset ()
5152 boost::shared_ptr<Region> region = _region_view->region ();
5153 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5156 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5157 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5164 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5166 framepos_t const p = _region_view->region()->position ();
5167 double const y = _region_view->midi_view()->y_position ();
5169 x1 = max ((framepos_t) 0, x1 - p);
5170 x2 = max ((framepos_t) 0, x2 - p);
5171 y1 = max (0.0, y1 - y);
5172 y2 = max (0.0, y2 - y);
5174 _region_view->update_drag_selection (
5175 _editor->sample_to_pixel (x1),
5176 _editor->sample_to_pixel (x2),
5179 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5184 MidiRubberbandSelectDrag::deselect_things ()
5189 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5190 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5193 _vertical_only = true;
5197 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5199 double const y = _region_view->midi_view()->y_position ();
5201 y1 = max (0.0, y1 - y);
5202 y2 = max (0.0, y2 - y);
5204 _region_view->update_vertical_drag_selection (
5207 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5212 MidiVerticalSelectDrag::deselect_things ()
5217 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5218 : RubberbandSelectDrag (e, i)
5224 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5226 if (drag_in_progress) {
5227 /* We just want to select things at the end of the drag, not during it */
5231 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5233 _editor->begin_reversible_command (_("rubberband selection"));
5234 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5235 _editor->commit_reversible_command ();
5239 EditorRubberbandSelectDrag::deselect_things ()
5241 if (!getenv("ARDOUR_SAE")) {
5242 _editor->selection->clear_tracks();
5244 _editor->selection->clear_regions();
5245 _editor->selection->clear_points ();
5246 _editor->selection->clear_lines ();
5249 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5257 NoteCreateDrag::~NoteCreateDrag ()
5263 NoteCreateDrag::grid_frames (framepos_t t) const
5266 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
5271 return _region_view->region_beats_to_region_frames (grid_beats);
5275 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5277 Drag::start_grab (event, cursor);
5279 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5281 framepos_t pf = _drags->current_pointer_frame ();
5282 framecnt_t const g = grid_frames (pf);
5284 /* Hack so that we always snap to the note that we are over, instead of snapping
5285 to the next one if we're more than halfway through the one we're over.
5287 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5291 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5293 MidiStreamView* sv = _region_view->midi_stream_view ();
5294 double const x = _editor->sample_to_pixel (_note[0]);
5295 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5297 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5298 _drag_rect->set_outline_all ();
5299 _drag_rect->set_outline_color (0xffffff99);
5300 _drag_rect->set_fill_color (0xffffff66);
5304 NoteCreateDrag::motion (GdkEvent* event, bool)
5306 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5307 double const x = _editor->sample_to_pixel (_note[1]);
5308 if (_note[1] > _note[0]) {
5309 _drag_rect->set_x1 (x);
5311 _drag_rect->set_x0 (x);
5316 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5318 if (!had_movement) {
5322 framepos_t const start = min (_note[0], _note[1]);
5323 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5325 framecnt_t const g = grid_frames (start);
5326 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5328 if (_editor->snap_mode() == SnapNormal && length < g) {
5329 length = g - one_tick;
5332 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5334 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5338 NoteCreateDrag::y_to_region (double y) const
5341 _region_view->get_canvas_group()->canvas_to_item (x, y);
5346 NoteCreateDrag::aborted (bool)
5351 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5356 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5360 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5362 Drag::start_grab (event, cursor);
5366 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5372 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5375 distance = _drags->current_pointer_x() - grab_x();
5376 len = ar->fade_in()->back()->when;
5378 distance = grab_x() - _drags->current_pointer_x();
5379 len = ar->fade_out()->back()->when;
5382 /* how long should it be ? */
5384 new_length = len + _editor->pixel_to_sample (distance);
5386 /* now check with the region that this is legal */
5388 new_length = ar->verify_xfade_bounds (new_length, start);
5391 arv->reset_fade_in_shape_width (ar, new_length);
5393 arv->reset_fade_out_shape_width (ar, new_length);
5398 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5404 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5407 distance = _drags->current_pointer_x() - grab_x();
5408 len = ar->fade_in()->back()->when;
5410 distance = grab_x() - _drags->current_pointer_x();
5411 len = ar->fade_out()->back()->when;
5414 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5416 _editor->begin_reversible_command ("xfade trim");
5417 ar->playlist()->clear_owned_changes ();
5420 ar->set_fade_in_length (new_length);
5422 ar->set_fade_out_length (new_length);
5425 /* Adjusting the xfade may affect other regions in the playlist, so we need
5426 to get undo Commands from the whole playlist rather than just the
5430 vector<Command*> cmds;
5431 ar->playlist()->rdiff (cmds);
5432 _editor->session()->add_commands (cmds);
5433 _editor->commit_reversible_command ();
5438 CrossfadeEdgeDrag::aborted (bool)
5441 arv->redraw_start_xfade ();
5443 arv->redraw_end_xfade ();