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 if (_operation == SelectionEndTrim)
4354 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4356 s->request_locate (_editor->get_selection().time.start());
4362 /* just a click, no pointer movement.
4365 if (_operation == SelectionExtend) {
4366 if (_time_selection_at_start) {
4367 framepos_t pos = adjusted_current_frame (event, false);
4368 framepos_t start = min (pos, start_at_start);
4369 framepos_t end = max (pos, end_at_start);
4370 _editor->selection->set (start, end);
4373 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4374 if (_editor->clicked_selection) {
4375 _editor->selection->remove (_editor->clicked_selection);
4378 if (!_editor->clicked_selection) {
4379 _editor->selection->clear_time();
4384 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4385 _editor->selection->set (_editor->clicked_axisview);
4388 if (s && s->get_play_range () && s->transport_rolling()) {
4389 s->request_stop (false, false);
4394 _editor->stop_canvas_autoscroll ();
4395 _editor->clicked_selection = 0;
4399 SelectionDrag::aborted (bool)
4404 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4405 : Drag (e, i, false),
4409 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4411 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4412 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4413 physical_screen_height (_editor->get_window())));
4414 _drag_rect->hide ();
4416 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4417 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4421 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4423 if (_editor->session() == 0) {
4427 Gdk::Cursor* cursor = 0;
4429 if (!_editor->temp_location) {
4430 _editor->temp_location = new Location (*_editor->session());
4433 switch (_operation) {
4434 case CreateRangeMarker:
4435 case CreateTransportMarker:
4436 case CreateCDMarker:
4438 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4443 cursor = _editor->cursors()->selector;
4447 Drag::start_grab (event, cursor);
4449 show_verbose_cursor_time (adjusted_current_frame (event));
4453 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4455 framepos_t start = 0;
4457 ArdourCanvas::Rectangle *crect;
4459 switch (_operation) {
4460 case CreateRangeMarker:
4461 crect = _editor->range_bar_drag_rect;
4463 case CreateTransportMarker:
4464 crect = _editor->transport_bar_drag_rect;
4466 case CreateCDMarker:
4467 crect = _editor->cd_marker_bar_drag_rect;
4470 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4475 framepos_t const pf = adjusted_current_frame (event);
4477 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4478 framepos_t grab = grab_frame ();
4479 _editor->snap_to (grab);
4481 if (pf < grab_frame()) {
4489 /* first drag: Either add to the selection
4490 or create a new selection.
4495 _editor->temp_location->set (start, end);
4499 update_item (_editor->temp_location);
4501 //_drag_rect->raise_to_top();
4507 _editor->temp_location->set (start, end);
4509 double x1 = _editor->sample_to_pixel (start);
4510 double x2 = _editor->sample_to_pixel (end);
4514 update_item (_editor->temp_location);
4517 show_verbose_cursor_time (pf);
4522 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4524 Location * newloc = 0;
4528 if (movement_occurred) {
4529 motion (event, false);
4532 switch (_operation) {
4533 case CreateRangeMarker:
4534 case CreateCDMarker:
4536 _editor->begin_reversible_command (_("new range marker"));
4537 XMLNode &before = _editor->session()->locations()->get_state();
4538 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4539 if (_operation == CreateCDMarker) {
4540 flags = Location::IsRangeMarker | Location::IsCDMarker;
4541 _editor->cd_marker_bar_drag_rect->hide();
4544 flags = Location::IsRangeMarker;
4545 _editor->range_bar_drag_rect->hide();
4547 newloc = new Location (
4548 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4551 _editor->session()->locations()->add (newloc, true);
4552 XMLNode &after = _editor->session()->locations()->get_state();
4553 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4554 _editor->commit_reversible_command ();
4558 case CreateTransportMarker:
4559 // popup menu to pick loop or punch
4560 _editor->new_transport_marker_context_menu (&event->button, _item);
4566 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4568 if (_operation == CreateTransportMarker) {
4570 /* didn't drag, so just locate */
4572 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4574 } else if (_operation == CreateCDMarker) {
4576 /* didn't drag, but mark is already created so do
4579 } else { /* operation == CreateRangeMarker */
4585 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4587 if (end == max_framepos) {
4588 end = _editor->session()->current_end_frame ();
4591 if (start == max_framepos) {
4592 start = _editor->session()->current_start_frame ();
4595 switch (_editor->mouse_mode) {
4597 /* find the two markers on either side and then make the selection from it */
4598 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4602 /* find the two markers on either side of the click and make the range out of it */
4603 _editor->selection->set (start, end);
4612 _editor->stop_canvas_autoscroll ();
4616 RangeMarkerBarDrag::aborted (bool)
4622 RangeMarkerBarDrag::update_item (Location* location)
4624 double const x1 = _editor->sample_to_pixel (location->start());
4625 double const x2 = _editor->sample_to_pixel (location->end());
4627 _drag_rect->set_x0 (x1);
4628 _drag_rect->set_x1 (x2);
4631 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4635 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4639 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4641 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4642 Drag::start_grab (event, _editor->cursors()->zoom_out);
4645 Drag::start_grab (event, _editor->cursors()->zoom_in);
4649 show_verbose_cursor_time (adjusted_current_frame (event));
4653 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4658 framepos_t const pf = adjusted_current_frame (event);
4660 framepos_t grab = grab_frame ();
4661 _editor->snap_to_with_modifier (grab, event);
4663 /* base start and end on initial click position */
4675 _editor->zoom_rect->show();
4676 _editor->zoom_rect->raise_to_top();
4679 _editor->reposition_zoom_rect(start, end);
4681 show_verbose_cursor_time (pf);
4686 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4688 if (movement_occurred) {
4689 motion (event, false);
4691 if (grab_frame() < last_pointer_frame()) {
4692 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4694 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4697 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4698 _editor->tav_zoom_step (_zoom_out);
4700 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4704 _editor->zoom_rect->hide();
4708 MouseZoomDrag::aborted (bool)
4710 _editor->zoom_rect->hide ();
4713 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4715 , _cumulative_dx (0)
4716 , _cumulative_dy (0)
4718 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4720 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4722 _region = &_primary->region_view ();
4723 _note_height = _region->midi_stream_view()->note_height ();
4727 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4729 Drag::start_grab (event);
4731 if (!(_was_selected = _primary->selected())) {
4733 /* tertiary-click means extend selection - we'll do that on button release,
4734 so don't add it here, because otherwise we make it hard to figure
4735 out the "extend-to" range.
4738 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4741 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4744 _region->note_selected (_primary, true);
4746 _region->unique_select (_primary);
4752 /** @return Current total drag x change in frames */
4754 NoteDrag::total_dx () const
4757 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4759 /* primary note time */
4760 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4762 /* new time of the primary note in session frames */
4763 frameoffset_t st = n + dx;
4765 framepos_t const rp = _region->region()->position ();
4767 /* prevent the note being dragged earlier than the region's position */
4770 /* snap and return corresponding delta */
4771 return _region->snap_frame_to_frame (st - rp) + rp - n;
4774 /** @return Current total drag y change in note number */
4776 NoteDrag::total_dy () const
4778 MidiStreamView* msv = _region->midi_stream_view ();
4779 double const y = _region->midi_view()->y_position ();
4780 /* new current note */
4781 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4783 n = max (msv->lowest_note(), n);
4784 n = min (msv->highest_note(), n);
4785 /* and work out delta */
4786 return n - msv->y_to_note (grab_y() - y);
4790 NoteDrag::motion (GdkEvent *, bool)
4792 /* Total change in x and y since the start of the drag */
4793 frameoffset_t const dx = total_dx ();
4794 int8_t const dy = total_dy ();
4796 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4797 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4798 double const tdy = -dy * _note_height - _cumulative_dy;
4801 _cumulative_dx += tdx;
4802 _cumulative_dy += tdy;
4804 int8_t note_delta = total_dy();
4806 _region->move_selection (tdx, tdy, note_delta);
4808 /* the new note value may be the same as the old one, but we
4809 * don't know what that means because the selection may have
4810 * involved more than one note and we might be doing something
4811 * odd with them. so show the note value anyway, always.
4815 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4817 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4818 (int) floor ((double)new_note));
4820 show_verbose_cursor_text (buf);
4825 NoteDrag::finished (GdkEvent* ev, bool moved)
4828 /* no motion - select note */
4830 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4831 _editor->current_mouse_mode() == Editing::MouseDraw) {
4833 if (_was_selected) {
4834 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4836 _region->note_deselected (_primary);
4839 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4840 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4842 if (!extend && !add && _region->selection_size() > 1) {
4843 _region->unique_select (_primary);
4844 } else if (extend) {
4845 _region->note_selected (_primary, true, true);
4847 /* it was added during button press */
4852 _region->note_dropped (_primary, total_dx(), total_dy());
4857 NoteDrag::aborted (bool)
4862 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4863 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4864 : Drag (editor, atv->base_item ())
4866 , _nothing_to_drag (false)
4868 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4869 y_origin = atv->y_position();
4870 setup (atv->lines ());
4873 /** Make an AutomationRangeDrag for region gain lines */
4874 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4875 : Drag (editor, rv->get_canvas_group ())
4877 , _nothing_to_drag (false)
4879 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4881 list<boost::shared_ptr<AutomationLine> > lines;
4882 lines.push_back (rv->get_gain_line ());
4883 y_origin = rv->get_time_axis_view().y_position();
4887 /** @param lines AutomationLines to drag.
4888 * @param offset Offset from the session start to the points in the AutomationLines.
4891 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4893 /* find the lines that overlap the ranges being dragged */
4894 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4895 while (i != lines.end ()) {
4896 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4899 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4901 /* check this range against all the AudioRanges that we are using */
4902 list<AudioRange>::const_iterator k = _ranges.begin ();
4903 while (k != _ranges.end()) {
4904 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4910 /* add it to our list if it overlaps at all */
4911 if (k != _ranges.end()) {
4916 _lines.push_back (n);
4922 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4926 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4928 return 1.0 - ((global_y - y_origin) / line->height());
4932 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4934 Drag::start_grab (event, cursor);
4936 /* Get line states before we start changing things */
4937 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4938 i->state = &i->line->get_state ();
4939 i->original_fraction = y_fraction (i->line, current_pointer_y());
4942 if (_ranges.empty()) {
4944 /* No selected time ranges: drag all points */
4945 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4946 uint32_t const N = i->line->npoints ();
4947 for (uint32_t j = 0; j < N; ++j) {
4948 i->points.push_back (i->line->nth (j));
4954 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4956 framecnt_t const half = (i->start + i->end) / 2;
4958 /* find the line that this audio range starts in */
4959 list<Line>::iterator j = _lines.begin();
4960 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4964 if (j != _lines.end()) {
4965 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4967 /* j is the line that this audio range starts in; fade into it;
4968 64 samples length plucked out of thin air.
4971 framepos_t a = i->start + 64;
4976 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4977 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4979 the_list->add (p, the_list->eval (p));
4980 the_list->add (q, the_list->eval (q));
4983 /* same thing for the end */
4986 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4990 if (j != _lines.end()) {
4991 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4993 /* j is the line that this audio range starts in; fade out of it;
4994 64 samples length plucked out of thin air.
4997 framepos_t b = i->end - 64;
5002 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5003 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5005 the_list->add (p, the_list->eval (p));
5006 the_list->add (q, the_list->eval (q));
5010 _nothing_to_drag = true;
5012 /* Find all the points that should be dragged and put them in the relevant
5013 points lists in the Line structs.
5016 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5018 uint32_t const N = i->line->npoints ();
5019 for (uint32_t j = 0; j < N; ++j) {
5021 /* here's a control point on this line */
5022 ControlPoint* p = i->line->nth (j);
5023 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5025 /* see if it's inside a range */
5026 list<AudioRange>::const_iterator k = _ranges.begin ();
5027 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5031 if (k != _ranges.end()) {
5032 /* dragging this point */
5033 _nothing_to_drag = false;
5034 i->points.push_back (p);
5040 if (_nothing_to_drag) {
5044 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5045 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5050 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5052 if (_nothing_to_drag) {
5056 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5057 float const f = y_fraction (l->line, current_pointer_y());
5058 /* we are ignoring x position for this drag, so we can just pass in anything */
5060 l->line->drag_motion (0, f, true, false, ignored);
5061 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5066 AutomationRangeDrag::finished (GdkEvent* event, bool)
5068 if (_nothing_to_drag) {
5072 motion (event, false);
5073 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5074 i->line->end_drag (false, 0);
5077 _editor->session()->commit_reversible_command ();
5081 AutomationRangeDrag::aborted (bool)
5083 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5088 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5090 , initial_time_axis_view (itav)
5092 /* note that time_axis_view may be null if the regionview was created
5093 * as part of a copy operation.
5095 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5096 layer = v->region()->layer ();
5097 initial_y = v->get_canvas_group()->position().y;
5098 initial_playlist = v->region()->playlist ();
5099 initial_position = v->region()->position ();
5100 initial_end = v->region()->position () + v->region()->length ();
5103 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5104 : Drag (e, i->canvas_item ())
5107 , _cumulative_dx (0)
5109 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5110 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5115 PatchChangeDrag::motion (GdkEvent* ev, bool)
5117 framepos_t f = adjusted_current_frame (ev);
5118 boost::shared_ptr<Region> r = _region_view->region ();
5119 f = max (f, r->position ());
5120 f = min (f, r->last_frame ());
5122 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5123 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5124 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5125 _cumulative_dx = dxu;
5129 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5131 if (!movement_occurred) {
5135 boost::shared_ptr<Region> r (_region_view->region ());
5136 framepos_t f = adjusted_current_frame (ev);
5137 f = max (f, r->position ());
5138 f = min (f, r->last_frame ());
5140 _region_view->move_patch_change (
5142 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5147 PatchChangeDrag::aborted (bool)
5149 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5153 PatchChangeDrag::setup_pointer_frame_offset ()
5155 boost::shared_ptr<Region> region = _region_view->region ();
5156 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5159 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5160 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5167 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5169 framepos_t const p = _region_view->region()->position ();
5170 double const y = _region_view->midi_view()->y_position ();
5172 x1 = max ((framepos_t) 0, x1 - p);
5173 x2 = max ((framepos_t) 0, x2 - p);
5174 y1 = max (0.0, y1 - y);
5175 y2 = max (0.0, y2 - y);
5177 _region_view->update_drag_selection (
5178 _editor->sample_to_pixel (x1),
5179 _editor->sample_to_pixel (x2),
5182 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5187 MidiRubberbandSelectDrag::deselect_things ()
5192 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5193 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5196 _vertical_only = true;
5200 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5202 double const y = _region_view->midi_view()->y_position ();
5204 y1 = max (0.0, y1 - y);
5205 y2 = max (0.0, y2 - y);
5207 _region_view->update_vertical_drag_selection (
5210 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5215 MidiVerticalSelectDrag::deselect_things ()
5220 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5221 : RubberbandSelectDrag (e, i)
5227 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5229 if (drag_in_progress) {
5230 /* We just want to select things at the end of the drag, not during it */
5234 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5236 _editor->begin_reversible_command (_("rubberband selection"));
5237 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5238 _editor->commit_reversible_command ();
5242 EditorRubberbandSelectDrag::deselect_things ()
5244 if (!getenv("ARDOUR_SAE")) {
5245 _editor->selection->clear_tracks();
5247 _editor->selection->clear_regions();
5248 _editor->selection->clear_points ();
5249 _editor->selection->clear_lines ();
5252 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5260 NoteCreateDrag::~NoteCreateDrag ()
5266 NoteCreateDrag::grid_frames (framepos_t t) const
5269 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
5274 return _region_view->region_beats_to_region_frames (grid_beats);
5278 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5280 Drag::start_grab (event, cursor);
5282 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5284 framepos_t pf = _drags->current_pointer_frame ();
5285 framecnt_t const g = grid_frames (pf);
5287 /* Hack so that we always snap to the note that we are over, instead of snapping
5288 to the next one if we're more than halfway through the one we're over.
5290 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5294 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5296 MidiStreamView* sv = _region_view->midi_stream_view ();
5297 double const x = _editor->sample_to_pixel (_note[0]);
5298 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5300 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5301 _drag_rect->set_outline_all ();
5302 _drag_rect->set_outline_color (0xffffff99);
5303 _drag_rect->set_fill_color (0xffffff66);
5307 NoteCreateDrag::motion (GdkEvent* event, bool)
5309 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5310 double const x = _editor->sample_to_pixel (_note[1]);
5311 if (_note[1] > _note[0]) {
5312 _drag_rect->set_x1 (x);
5314 _drag_rect->set_x0 (x);
5319 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5321 if (!had_movement) {
5325 framepos_t const start = min (_note[0], _note[1]);
5326 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5328 framecnt_t const g = grid_frames (start);
5329 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5331 if (_editor->snap_mode() == SnapNormal && length < g) {
5332 length = g - one_tick;
5335 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5337 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5341 NoteCreateDrag::y_to_region (double y) const
5344 _region_view->get_canvas_group()->canvas_to_item (x, y);
5349 NoteCreateDrag::aborted (bool)
5354 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5359 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5363 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5365 Drag::start_grab (event, cursor);
5369 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5375 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5378 distance = _drags->current_pointer_x() - grab_x();
5379 len = ar->fade_in()->back()->when;
5381 distance = grab_x() - _drags->current_pointer_x();
5382 len = ar->fade_out()->back()->when;
5385 /* how long should it be ? */
5387 new_length = len + _editor->pixel_to_sample (distance);
5389 /* now check with the region that this is legal */
5391 new_length = ar->verify_xfade_bounds (new_length, start);
5394 arv->reset_fade_in_shape_width (ar, new_length);
5396 arv->reset_fade_out_shape_width (ar, new_length);
5401 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5407 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5410 distance = _drags->current_pointer_x() - grab_x();
5411 len = ar->fade_in()->back()->when;
5413 distance = grab_x() - _drags->current_pointer_x();
5414 len = ar->fade_out()->back()->when;
5417 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5419 _editor->begin_reversible_command ("xfade trim");
5420 ar->playlist()->clear_owned_changes ();
5423 ar->set_fade_in_length (new_length);
5425 ar->set_fade_out_length (new_length);
5428 /* Adjusting the xfade may affect other regions in the playlist, so we need
5429 to get undo Commands from the whole playlist rather than just the
5433 vector<Command*> cmds;
5434 ar->playlist()->rdiff (cmds);
5435 _editor->session()->add_commands (cmds);
5436 _editor->commit_reversible_command ();
5441 CrossfadeEdgeDrag::aborted (bool)
5444 arv->redraw_start_xfade ();
5446 arv->redraw_end_xfade ();
5450 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item)
5451 : Drag (e, item, true)
5457 RegionCutDrag::motion (GdkEvent*, bool)
5462 RegionCutDrag::finished (GdkEvent*, bool)
5464 _editor->split_region ();
5468 RegionCutDrag::aborted (bool)