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 cerr << "Aborting drag\n";
99 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
104 if (!_drags.empty ()) {
105 _editor->set_follow_playhead (_old_follow_playhead, false);
114 DragManager::add (Drag* d)
116 d->set_manager (this);
117 _drags.push_back (d);
121 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
123 d->set_manager (this);
124 _drags.push_back (d);
129 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
131 /* Prevent follow playhead during the drag to be nice to the user */
132 _old_follow_playhead = _editor->follow_playhead ();
133 _editor->set_follow_playhead (false);
135 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
137 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
138 (*i)->start_grab (e, c);
142 /** Call end_grab for each active drag.
143 * @return true if any drag reported movement having occurred.
146 DragManager::end_grab (GdkEvent* e)
151 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
152 bool const t = (*i)->end_grab (e);
163 _editor->set_follow_playhead (_old_follow_playhead, false);
169 DragManager::mark_double_click ()
171 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
172 (*i)->set_double_click (true);
177 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
181 /* calling this implies that we expect the event to have canvas
184 * Can we guarantee that this is true?
187 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
189 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
190 bool const t = (*i)->motion_handler (e, from_autoscroll);
191 /* run all handlers; return true if at least one of them
192 returns true (indicating that the event has been handled).
204 DragManager::have_item (ArdourCanvas::Item* i) const
206 list<Drag*>::const_iterator j = _drags.begin ();
207 while (j != _drags.end() && (*j)->item () != i) {
211 return j != _drags.end ();
214 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
217 , _pointer_frame_offset (0)
218 , _trackview_only (trackview_only)
219 , _move_threshold_passed (false)
220 , _was_double_click (false)
221 , _raw_grab_frame (0)
223 , _last_pointer_frame (0)
229 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
242 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
244 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
246 if (Keyboard::is_button2_event (&event->button)) {
247 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
248 _y_constrained = true;
249 _x_constrained = false;
251 _y_constrained = false;
252 _x_constrained = true;
255 _x_constrained = false;
256 _y_constrained = false;
259 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
260 setup_pointer_frame_offset ();
261 _grab_frame = adjusted_frame (_raw_grab_frame, event);
262 _last_pointer_frame = _grab_frame;
263 _last_pointer_x = _grab_x;
265 if (_trackview_only) {
266 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
269 _last_pointer_y = _grab_y;
274 /* CAIROCANVAS need a variant here that passes *cursor */
276 _editor->push_canvas_cursor (cursor);
279 if (_editor->session() && _editor->session()->transport_rolling()) {
282 _was_rolling = false;
285 switch (_editor->snap_type()) {
286 case SnapToRegionStart:
287 case SnapToRegionEnd:
288 case SnapToRegionSync:
289 case SnapToRegionBoundary:
290 _editor->build_region_boundary_cache ();
297 /** Call to end a drag `successfully'. Ungrabs item and calls
298 * subclass' finished() method.
300 * @param event GDK event, or 0.
301 * @return true if some movement occurred, otherwise false.
304 Drag::end_grab (GdkEvent* event)
306 _editor->stop_canvas_autoscroll ();
310 finished (event, _move_threshold_passed);
312 _editor->verbose_cursor()->hide ();
313 _editor->pop_canvas_cursor ();
315 return _move_threshold_passed;
319 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
323 if (f > _pointer_frame_offset) {
324 pos = f - _pointer_frame_offset;
328 _editor->snap_to_with_modifier (pos, event);
335 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
337 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
341 Drag::current_pointer_y () const
343 if (!_trackview_only) {
344 return _drags->current_pointer_y ();
347 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
351 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
353 /* check to see if we have moved in any way that matters since the last motion event */
354 if (_move_threshold_passed &&
355 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
356 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
360 pair<framecnt_t, int> const threshold = move_threshold ();
362 bool const old_move_threshold_passed = _move_threshold_passed;
364 if (!from_autoscroll && !_move_threshold_passed) {
366 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
367 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
369 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
372 if (active (_editor->mouse_mode) && _move_threshold_passed) {
374 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
375 if (!from_autoscroll) {
376 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
379 if (!_editor->autoscroll_active() || from_autoscroll) {
380 motion (event, _move_threshold_passed != old_move_threshold_passed);
382 _last_pointer_x = _drags->current_pointer_x ();
383 _last_pointer_y = current_pointer_y ();
384 _last_pointer_frame = adjusted_current_frame (event);
394 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
402 aborted (_move_threshold_passed);
404 _editor->stop_canvas_autoscroll ();
405 _editor->verbose_cursor()->hide ();
409 Drag::show_verbose_cursor_time (framepos_t frame)
411 _editor->verbose_cursor()->set_time (frame);
412 _editor->verbose_cursor()->show ();
416 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
418 _editor->verbose_cursor()->set_duration (start, end);
419 _editor->verbose_cursor()->show ();
423 Drag::show_verbose_cursor_text (string const & text)
425 _editor->verbose_cursor()->set (text);
426 _editor->verbose_cursor()->show ();
429 boost::shared_ptr<Region>
430 Drag::add_midi_region (MidiTimeAxisView* view)
432 if (_editor->session()) {
433 const TempoMap& map (_editor->session()->tempo_map());
434 framecnt_t pos = grab_frame();
435 const Meter& m = map.meter_at (pos);
436 /* not that the frame rate used here can be affected by pull up/down which
439 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
440 return view->add_region (grab_frame(), len, true);
443 return boost::shared_ptr<Region>();
446 struct EditorOrderTimeAxisViewSorter {
447 bool operator() (TimeAxisView* a, TimeAxisView* b) {
448 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
449 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
451 return ra->route()->order_key () < rb->route()->order_key ();
455 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
459 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
461 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
462 as some of the regions we are dragging may be on such tracks.
465 TrackViewList track_views = _editor->track_views;
466 track_views.sort (EditorOrderTimeAxisViewSorter ());
468 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
469 _time_axis_views.push_back (*i);
471 TimeAxisView::Children children_list = (*i)->get_child_list ();
472 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
473 _time_axis_views.push_back (j->get());
477 /* the list of views can be empty at this point if this is a region list-insert drag
480 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
481 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
484 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
488 RegionDrag::region_going_away (RegionView* v)
490 list<DraggingView>::iterator i = _views.begin ();
491 while (i != _views.end() && i->view != v) {
495 if (i != _views.end()) {
500 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
501 * or -1 if it is not found.
504 RegionDrag::find_time_axis_view (TimeAxisView* t) const
507 int const N = _time_axis_views.size ();
508 while (i < N && _time_axis_views[i] != t) {
519 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
520 : RegionDrag (e, i, p, v)
523 , _last_pointer_time_axis_view (0)
524 , _last_pointer_layer (0)
526 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
530 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
532 Drag::start_grab (event, cursor);
534 show_verbose_cursor_time (_last_frame_position);
536 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
538 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
539 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
544 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
546 /* compute the amount of pointer motion in frames, and where
547 the region would be if we moved it by that much.
549 *pending_region_position = adjusted_current_frame (event);
551 framepos_t sync_frame;
552 framecnt_t sync_offset;
555 sync_offset = _primary->region()->sync_offset (sync_dir);
557 /* we don't handle a sync point that lies before zero.
559 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
561 sync_frame = *pending_region_position + (sync_dir*sync_offset);
563 _editor->snap_to_with_modifier (sync_frame, event);
565 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
568 *pending_region_position = _last_frame_position;
571 if (*pending_region_position > max_framepos - _primary->region()->length()) {
572 *pending_region_position = _last_frame_position;
577 /* in locked edit mode, reverse the usual meaning of _x_constrained */
578 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
580 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
582 /* x movement since last time (in pixels) */
583 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
585 /* total x movement */
586 framecnt_t total_dx = *pending_region_position;
587 if (regions_came_from_canvas()) {
588 total_dx = total_dx - grab_frame ();
591 /* check that no regions have gone off the start of the session */
592 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
593 if ((i->view->region()->position() + total_dx) < 0) {
595 *pending_region_position = _last_frame_position;
606 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
608 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
609 int const n = i->time_axis_view + delta_track;
610 if (n < 0 || n >= int (_time_axis_views.size())) {
611 /* off the top or bottom track */
615 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
616 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
617 /* not a track, or the wrong type */
621 double const l = i->layer + delta_layer;
623 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
624 mode to allow the user to place a region below another on layer 0.
626 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
627 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
628 If it has, the layers will be munged later anyway, so it's ok.
634 /* all regions being dragged are ok with this change */
639 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
641 double delta_layer = 0;
642 int delta_time_axis_view = 0;
644 assert (!_views.empty ());
646 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
648 /* Find the TimeAxisView that the pointer is now over */
649 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
650 TimeAxisView* tv = r.first;
652 if (tv && tv->view()) {
653 double layer = r.second;
655 if (first_move && tv->view()->layer_display() == Stacked) {
656 tv->view()->set_layer_display (Expanded);
659 /* Here's the current pointer position in terms of time axis view and layer */
660 int const current_pointer_time_axis_view = find_time_axis_view (tv);
661 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
663 /* Work out the change in y */
665 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
666 delta_layer = current_pointer_layer - _last_pointer_layer;
669 /* Work out the change in x */
670 framepos_t pending_region_position;
671 double const x_delta = compute_x_delta (event, &pending_region_position);
672 _last_frame_position = pending_region_position;
674 /* Verify change in y */
675 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
676 /* this y movement is not allowed, so do no y movement this time */
677 delta_time_axis_view = 0;
681 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
682 /* haven't reached next snap point, and we're not switching
683 trackviews nor layers. nothing to do.
688 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
690 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
692 RegionView* rv = i->view;
694 if (rv->region()->locked() || rv->region()->video_locked()) {
701 /* reparent the regionview into a group above all
705 ArdourCanvas::Item* rvg = rv->get_canvas_group();
706 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
707 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
708 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
709 /* move the item so that it continues to appear at the
710 same location now that its parent has changed.
712 rvg->move (rv_canvas_offset - dmg_canvas_offset);
715 /* If we have moved tracks, we'll fudge the layer delta so that the
716 region gets moved back onto layer 0 on its new track; this avoids
717 confusion when dragging regions from non-zero layers onto different
720 double this_delta_layer = delta_layer;
721 if (delta_time_axis_view != 0) {
722 this_delta_layer = - i->layer;
729 if (i->time_axis_view >= 0) {
730 track_index = i->time_axis_view + delta_time_axis_view;
732 track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
735 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
739 /* The TimeAxisView that this region is now over */
740 TimeAxisView* current_tv = _time_axis_views[track_index];
742 /* Ensure it is moved from stacked -> expanded if appropriate */
743 if (current_tv->view()->layer_display() == Stacked) {
744 current_tv->view()->set_layer_display (Expanded);
747 /* We're only allowed to go -ve in layer on Expanded views */
748 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
749 this_delta_layer = - i->layer;
753 rv->set_height (current_tv->view()->child_height ());
755 /* Update show/hidden status as the region view may have come from a hidden track,
756 or have moved to one.
758 if (current_tv->hidden ()) {
759 rv->get_canvas_group()->hide ();
761 rv->get_canvas_group()->show ();
764 /* Update the DraggingView */
765 i->time_axis_view = track_index;
766 i->layer += this_delta_layer;
769 _editor->mouse_brush_insert_region (rv, pending_region_position);
773 /* Get the y coordinate of the top of the track that this region is now over */
774 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
776 /* And adjust for the layer that it should be on */
777 StreamView* cv = current_tv->view ();
778 switch (cv->layer_display ()) {
782 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
785 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
789 /* need to get the parent of the regionview
790 * canvas group and get its position in
791 * equivalent coordinate space as the trackview
792 * we are now dragging over.
795 /* Now move the region view */
796 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->canvas_origin().y);
800 /* Only move the region into the empty dropzone at the bottom if the pointer
804 if (current_pointer_y() >= 0) {
806 Coord last_track_bottom_edge;
807 if (!_time_axis_views.empty()) {
808 TimeAxisView* last = _time_axis_views.back();
809 last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
811 last_track_bottom_edge = 0;
814 rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y);
815 i->time_axis_view = -1;
819 } /* foreach region */
821 _total_x_delta += x_delta;
823 if (x_delta != 0 && !_brushing) {
824 show_verbose_cursor_time (_last_frame_position);
827 _last_pointer_time_axis_view += delta_time_axis_view;
828 _last_pointer_layer += delta_layer;
832 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
834 if (_copy && first_move) {
836 /* duplicate the regionview(s) and region(s) */
838 list<DraggingView> new_regionviews;
840 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
842 RegionView* rv = i->view;
843 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
844 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
846 const boost::shared_ptr<const Region> original = rv->region();
847 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
848 region_copy->set_position (original->position());
852 boost::shared_ptr<AudioRegion> audioregion_copy
853 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
855 nrv = new AudioRegionView (*arv, audioregion_copy);
857 boost::shared_ptr<MidiRegion> midiregion_copy
858 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
859 nrv = new MidiRegionView (*mrv, midiregion_copy);
864 nrv->get_canvas_group()->show ();
865 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
867 /* swap _primary to the copy */
869 if (rv == _primary) {
873 /* ..and deselect the one we copied */
875 rv->set_selected (false);
878 if (!new_regionviews.empty()) {
880 /* reflect the fact that we are dragging the copies */
882 _views = new_regionviews;
884 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
888 RegionMotionDrag::motion (event, first_move);
892 RegionMotionDrag::finished (GdkEvent *, bool)
894 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
899 if ((*i)->view()->layer_display() == Expanded) {
900 (*i)->view()->set_layer_display (Stacked);
906 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
908 RegionMotionDrag::finished (ev, movement_occurred);
910 if (!movement_occurred) {
914 if (was_double_click() && !_views.empty()) {
915 DraggingView dv = _views.front();
916 dv.view->show_region_editor ();
923 /* reverse this here so that we have the correct logic to finalize
927 if (Config->get_edit_mode() == Lock) {
928 _x_constrained = !_x_constrained;
931 assert (!_views.empty ());
933 /* We might have hidden region views so that they weren't visible during the drag
934 (when they have been reparented). Now everything can be shown again, as region
935 views are back in their track parent groups.
937 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
938 i->view->get_canvas_group()->show ();
941 bool const changed_position = (_last_frame_position != _primary->region()->position());
942 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
943 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
963 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
967 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
969 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
974 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
975 list<boost::shared_ptr<AudioTrack> > audio_tracks;
976 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
977 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
979 rtav->set_height (original->current_height());
983 ChanCount one_midi_port (DataType::MIDI, 1);
984 list<boost::shared_ptr<MidiTrack> > midi_tracks;
985 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
986 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
988 rtav->set_height (original->current_height());
993 error << _("Could not create new track after region placed in the drop zone") << endmsg;
999 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1001 RegionSelection new_views;
1002 PlaylistSet modified_playlists;
1003 RouteTimeAxisView* new_time_axis_view = 0;
1006 /* all changes were made during motion event handlers */
1008 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1012 _editor->commit_reversible_command ();
1016 if (_x_constrained) {
1017 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1019 _editor->begin_reversible_command (Operations::region_copy);
1022 /* insert the regions into their new playlists */
1023 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1025 RouteTimeAxisView* dest_rtv = 0;
1027 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1033 if (changed_position && !_x_constrained) {
1034 where = i->view->region()->position() - drag_delta;
1036 where = i->view->region()->position();
1039 if (i->time_axis_view < 0) {
1040 if (!new_time_axis_view) {
1041 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1043 dest_rtv = new_time_axis_view;
1045 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1048 if (dest_rtv != 0) {
1049 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1050 if (new_view != 0) {
1051 new_views.push_back (new_view);
1055 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1056 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1059 list<DraggingView>::const_iterator next = i;
1065 /* If we've created new regions either by copying or moving
1066 to a new track, we want to replace the old selection with the new ones
1069 if (new_views.size() > 0) {
1070 _editor->selection->set (new_views);
1073 /* write commands for the accumulated diffs for all our modified playlists */
1074 add_stateful_diff_commands_for_playlists (modified_playlists);
1076 _editor->commit_reversible_command ();
1080 RegionMoveDrag::finished_no_copy (
1081 bool const changed_position,
1082 bool const changed_tracks,
1083 framecnt_t const drag_delta
1086 RegionSelection new_views;
1087 PlaylistSet modified_playlists;
1088 PlaylistSet frozen_playlists;
1089 set<RouteTimeAxisView*> views_to_update;
1090 RouteTimeAxisView* new_time_axis_view = 0;
1093 /* all changes were made during motion event handlers */
1094 _editor->commit_reversible_command ();
1098 if (_x_constrained) {
1099 _editor->begin_reversible_command (_("fixed time region drag"));
1101 _editor->begin_reversible_command (Operations::region_drag);
1104 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1106 RegionView* rv = i->view;
1107 RouteTimeAxisView* dest_rtv = 0;
1109 if (rv->region()->locked() || rv->region()->video_locked()) {
1114 if (i->time_axis_view < 0) {
1115 if (!new_time_axis_view) {
1116 new_time_axis_view = create_destination_time_axis (rv->region(), i->initial_time_axis_view);
1118 dest_rtv = new_time_axis_view;
1120 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1125 double const dest_layer = i->layer;
1127 views_to_update.insert (dest_rtv);
1131 if (changed_position && !_x_constrained) {
1132 where = rv->region()->position() - drag_delta;
1134 where = rv->region()->position();
1137 if (changed_tracks) {
1139 /* insert into new playlist */
1141 RegionView* new_view = insert_region_into_playlist (
1142 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1145 if (new_view == 0) {
1150 new_views.push_back (new_view);
1152 /* remove from old playlist */
1154 /* the region that used to be in the old playlist is not
1155 moved to the new one - we use a copy of it. as a result,
1156 any existing editor for the region should no longer be
1159 rv->hide_region_editor();
1162 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1166 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1168 /* this movement may result in a crossfade being modified, or a layering change,
1169 so we need to get undo data from the playlist as well as the region.
1172 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1174 playlist->clear_changes ();
1177 rv->region()->clear_changes ();
1180 motion on the same track. plonk the previously reparented region
1181 back to its original canvas group (its streamview).
1182 No need to do anything for copies as they are fake regions which will be deleted.
1185 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1186 rv->get_canvas_group()->set_y_position (i->initial_y);
1189 /* just change the model */
1190 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1191 playlist->set_layer (rv->region(), dest_layer);
1194 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1196 r = frozen_playlists.insert (playlist);
1199 playlist->freeze ();
1202 rv->region()->set_position (where);
1204 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1207 if (changed_tracks) {
1209 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1210 was selected in all of them, then removing it from a playlist will have removed all
1211 trace of it from _views (i.e. there were N regions selected, we removed 1,
1212 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1213 corresponding regionview, and _views is now empty).
1215 This could have invalidated any and all iterators into _views.
1217 The heuristic we use here is: if the region selection is empty, break out of the loop
1218 here. if the region selection is not empty, then restart the loop because we know that
1219 we must have removed at least the region(view) we've just been working on as well as any
1220 that we processed on previous iterations.
1222 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1223 we can just iterate.
1227 if (_views.empty()) {
1238 /* If we've created new regions either by copying or moving
1239 to a new track, we want to replace the old selection with the new ones
1242 if (new_views.size() > 0) {
1243 _editor->selection->set (new_views);
1246 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1250 /* write commands for the accumulated diffs for all our modified playlists */
1251 add_stateful_diff_commands_for_playlists (modified_playlists);
1253 _editor->commit_reversible_command ();
1255 /* We have futzed with the layering of canvas items on our streamviews.
1256 If any region changed layer, this will have resulted in the stream
1257 views being asked to set up their region views, and all will be well.
1258 If not, we might now have badly-ordered region views. Ask the StreamViews
1259 involved to sort themselves out, just in case.
1262 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1263 (*i)->view()->playlist_layered ((*i)->track ());
1267 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1268 * @param region Region to remove.
1269 * @param playlist playlist To remove from.
1270 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1271 * that clear_changes () is only called once per playlist.
1274 RegionMoveDrag::remove_region_from_playlist (
1275 boost::shared_ptr<Region> region,
1276 boost::shared_ptr<Playlist> playlist,
1277 PlaylistSet& modified_playlists
1280 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1283 playlist->clear_changes ();
1286 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1290 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1291 * clearing the playlist's diff history first if necessary.
1292 * @param region Region to insert.
1293 * @param dest_rtv Destination RouteTimeAxisView.
1294 * @param dest_layer Destination layer.
1295 * @param where Destination position.
1296 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1297 * that clear_changes () is only called once per playlist.
1298 * @return New RegionView, or 0 if no insert was performed.
1301 RegionMoveDrag::insert_region_into_playlist (
1302 boost::shared_ptr<Region> region,
1303 RouteTimeAxisView* dest_rtv,
1306 PlaylistSet& modified_playlists
1309 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1310 if (!dest_playlist) {
1314 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1315 _new_region_view = 0;
1316 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1318 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1319 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1321 dest_playlist->clear_changes ();
1324 dest_playlist->add_region (region, where);
1326 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1327 dest_playlist->set_layer (region, dest_layer);
1332 assert (_new_region_view);
1334 return _new_region_view;
1338 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1340 _new_region_view = rv;
1344 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1346 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1347 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1349 _editor->session()->add_command (c);
1358 RegionMoveDrag::aborted (bool movement_occurred)
1362 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1369 RegionMotionDrag::aborted (movement_occurred);
1374 RegionMotionDrag::aborted (bool)
1376 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1378 StreamView* sview = (*i)->view();
1381 if (sview->layer_display() == Expanded) {
1382 sview->set_layer_display (Stacked);
1387 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1388 RegionView* rv = i->view;
1389 TimeAxisView* tv = &(rv->get_time_axis_view ());
1390 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1392 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1393 rv->get_canvas_group()->set_y_position (0);
1395 rv->move (-_total_x_delta, 0);
1396 rv->set_height (rtv->view()->child_height ());
1400 /** @param b true to brush, otherwise false.
1401 * @param c true to make copies of the regions being moved, otherwise false.
1403 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1404 : RegionMotionDrag (e, i, p, v, b),
1407 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1410 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1411 if (rtv && rtv->is_track()) {
1412 speed = rtv->track()->speed ();
1415 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1419 RegionMoveDrag::setup_pointer_frame_offset ()
1421 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1424 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1425 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1427 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1429 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1430 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1432 _primary = v->view()->create_region_view (r, false, false);
1434 _primary->get_canvas_group()->show ();
1435 _primary->set_position (pos, 0);
1436 _views.push_back (DraggingView (_primary, this, v));
1438 _last_frame_position = pos;
1440 _item = _primary->get_canvas_group ();
1444 RegionInsertDrag::finished (GdkEvent *, bool)
1446 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1448 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1449 _primary->get_canvas_group()->set_y_position (0);
1451 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1453 _editor->begin_reversible_command (Operations::insert_region);
1454 playlist->clear_changes ();
1455 playlist->add_region (_primary->region (), _last_frame_position);
1457 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1458 if (Config->get_edit_mode() == Ripple) {
1459 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1462 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1463 _editor->commit_reversible_command ();
1471 RegionInsertDrag::aborted (bool)
1478 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1479 : RegionMoveDrag (e, i, p, v, false, false)
1481 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1484 struct RegionSelectionByPosition {
1485 bool operator() (RegionView*a, RegionView* b) {
1486 return a->region()->position () < b->region()->position();
1491 RegionSpliceDrag::motion (GdkEvent* event, bool)
1493 /* Which trackview is this ? */
1495 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1496 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1498 /* The region motion is only processed if the pointer is over
1502 if (!tv || !tv->is_track()) {
1503 /* To make sure we hide the verbose canvas cursor when the mouse is
1504 not held over an audio track.
1506 _editor->verbose_cursor()->hide ();
1509 _editor->verbose_cursor()->show ();
1514 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1520 RegionSelection copy;
1521 _editor->selection->regions.by_position(copy);
1523 framepos_t const pf = adjusted_current_frame (event);
1525 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1527 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1533 boost::shared_ptr<Playlist> playlist;
1535 if ((playlist = atv->playlist()) == 0) {
1539 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1544 if (pf < (*i)->region()->last_frame() + 1) {
1548 if (pf > (*i)->region()->first_frame()) {
1554 playlist->shuffle ((*i)->region(), dir);
1559 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1561 RegionMoveDrag::finished (event, movement_occurred);
1565 RegionSpliceDrag::aborted (bool)
1575 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1578 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1580 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1581 RegionSelection to_ripple;
1582 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1583 if ((*i)->position() >= where) {
1584 to_ripple.push_back (rtv->view()->find_view(*i));
1588 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1589 if (!exclude.contains (*i)) {
1590 // the selection has already been added to _views
1592 if (drag_in_progress) {
1593 // do the same things that RegionMotionDrag::motion does when
1594 // first_move is true, for the region views that we're adding
1595 // to _views this time
1598 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1599 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1600 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1601 rvg->reparent (_editor->_drag_motion_group);
1603 // we only need to move in the y direction
1604 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1609 _views.push_back (DraggingView (*i, this, tav));
1615 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1618 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1619 // we added all the regions after the selection
1621 std::list<DraggingView>::iterator to_erase = i++;
1622 if (!_editor->selection->regions.contains (to_erase->view)) {
1623 // restore the non-selected regions to their original playlist & positions,
1624 // and then ripple them back by the length of the regions that were dragged away
1625 // do the same things as RegionMotionDrag::aborted
1627 RegionView *rv = to_erase->view;
1628 TimeAxisView* tv = &(rv->get_time_axis_view ());
1629 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1632 // plonk them back onto their own track
1633 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1634 rv->get_canvas_group()->set_y_position (0);
1638 // move the underlying region to match the view
1639 rv->region()->set_position (rv->region()->position() + amount);
1641 // restore the view to match the underlying region's original position
1642 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
1645 rv->set_height (rtv->view()->child_height ());
1646 _views.erase (to_erase);
1652 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const
1654 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) {
1656 return allow_moves_across_tracks;
1664 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1665 : RegionMoveDrag (e, i, p, v, false, false)
1667 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
1668 // compute length of selection
1669 RegionSelection selected_regions = _editor->selection->regions;
1670 selection_length = selected_regions.end_frame() - selected_regions.start();
1672 // we'll only allow dragging to another track in ripple mode if all the regions
1673 // being dragged start off on the same track
1674 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
1677 exclude = new RegionList;
1678 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
1679 exclude->push_back((*i)->region());
1682 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
1683 RegionSelection copy;
1684 selected_regions.by_position(copy); // get selected regions sorted by position into copy
1686 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
1687 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1689 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1690 // find ripple start point on each applicable playlist
1691 RegionView *first_selected_on_this_track = NULL;
1692 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1693 if ((*i)->region()->playlist() == (*pi)) {
1694 // region is on this playlist - it's the first, because they're sorted
1695 first_selected_on_this_track = *i;
1699 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
1700 add_all_after_to_views (
1701 &first_selected_on_this_track->get_time_axis_view(),
1702 first_selected_on_this_track->region()->position(),
1703 selected_regions, false);
1706 if (allow_moves_across_tracks) {
1707 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
1715 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
1717 /* Which trackview is this ? */
1719 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1720 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1722 /* The region motion is only processed if the pointer is over
1726 if (!tv || !tv->is_track()) {
1727 /* To make sure we hide the verbose canvas cursor when the mouse is
1728 not held over an audiotrack.
1730 _editor->verbose_cursor()->hide ();
1734 framepos_t where = adjusted_current_frame (event);
1735 assert (where >= 0);
1737 double delta = compute_x_delta (event, &after);
1739 framecnt_t amount = _editor->pixel_to_sample (delta);
1741 if (allow_moves_across_tracks) {
1742 // all the originally selected regions were on the same track
1744 framecnt_t adjust = 0;
1745 if (prev_tav && tv != prev_tav) {
1746 // dragged onto a different track
1747 // remove the unselected regions from _views, restore them to their original positions
1748 // and add the regions after the drop point on the new playlist to _views instead.
1749 // undo the effect of rippling the previous playlist, and include the effect of removing
1750 // the dragged region(s) from this track
1752 remove_unselected_from_views (prev_amount, false);
1753 // ripple previous playlist according to the regions that have been removed onto the new playlist
1754 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
1757 // move just the selected regions
1758 RegionMoveDrag::motion(event, first_move);
1760 // ensure that the ripple operation on the new playlist inserts selection_length time
1761 adjust = selection_length;
1762 // ripple the new current playlist
1763 tv->playlist()->ripple (where, amount+adjust, exclude);
1765 // add regions after point where drag entered this track to subsequent ripples
1766 add_all_after_to_views (tv, where, _editor->selection->regions, true);
1769 // motion on same track
1770 RegionMoveDrag::motion(event, first_move);
1774 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
1775 prev_position = where;
1777 // selection encompasses multiple tracks - just drag
1778 // cross-track drags are forbidden
1779 RegionMoveDrag::motion(event, first_move);
1782 if (!_x_constrained) {
1783 prev_amount += amount;
1786 _last_frame_position = after;
1790 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
1792 if (!movement_occurred) {
1796 _editor->begin_reversible_command(_("Ripple drag"));
1798 // remove the regions being rippled from the dragging view, updating them to
1799 // their new positions
1800 remove_unselected_from_views (prev_amount, true);
1802 if (allow_moves_across_tracks) {
1804 // if regions were dragged across tracks, we've rippled any later
1805 // regions on the track the regions were dragged off, so we need
1806 // to add the original track to the undo record
1807 orig_tav->playlist()->clear_changes();
1808 vector<Command*> cmds;
1809 orig_tav->playlist()->rdiff (cmds);
1810 _editor->session()->add_commands (cmds);
1812 if (prev_tav && prev_tav != orig_tav) {
1813 prev_tav->playlist()->clear_changes();
1814 vector<Command*> cmds;
1815 prev_tav->playlist()->rdiff (cmds);
1816 _editor->session()->add_commands (cmds);
1819 // selection spanned multiple tracks - all will need adding to undo record
1821 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
1822 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1824 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1825 (*pi)->clear_changes();
1826 vector<Command*> cmds;
1827 (*pi)->rdiff (cmds);
1828 _editor->session()->add_commands (cmds);
1832 // other modified playlists are added to undo by RegionMoveDrag::finished()
1833 RegionMoveDrag::finished (event, movement_occurred);
1834 _editor->commit_reversible_command();
1838 RegionRippleDrag::aborted (bool movement_occurred)
1840 RegionMoveDrag::aborted (movement_occurred);
1845 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1847 _view (dynamic_cast<MidiTimeAxisView*> (v))
1849 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1855 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1858 _region = add_midi_region (_view);
1859 _view->playlist()->freeze ();
1862 framepos_t const f = adjusted_current_frame (event);
1863 if (f < grab_frame()) {
1864 _region->set_position (f);
1867 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1868 so that if this region is duplicated, its duplicate starts on
1869 a snap point rather than 1 frame after a snap point. Otherwise things get
1870 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1871 place snapped notes at the start of the region.
1874 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1875 _region->set_length (len < 1 ? 1 : len);
1881 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1883 if (!movement_occurred) {
1884 add_midi_region (_view);
1886 _view->playlist()->thaw ();
1891 RegionCreateDrag::aborted (bool)
1894 _view->playlist()->thaw ();
1900 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1904 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1908 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1910 Gdk::Cursor* cursor;
1911 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1913 float x_fraction = cnote->mouse_x_fraction ();
1915 if (x_fraction > 0.0 && x_fraction < 0.25) {
1916 cursor = _editor->cursors()->left_side_trim;
1918 cursor = _editor->cursors()->right_side_trim;
1921 Drag::start_grab (event, cursor);
1923 region = &cnote->region_view();
1925 double const region_start = region->get_position_pixels();
1926 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1928 if (grab_x() <= middle_point) {
1929 cursor = _editor->cursors()->left_side_trim;
1932 cursor = _editor->cursors()->right_side_trim;
1938 if (event->motion.state & Keyboard::PrimaryModifier) {
1944 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1946 if (ms.size() > 1) {
1947 /* has to be relative, may make no sense otherwise */
1951 /* select this note; if it is already selected, preserve the existing selection,
1952 otherwise make this note the only one selected.
1954 region->note_selected (cnote, cnote->selected ());
1956 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1957 MidiRegionSelection::iterator next;
1960 (*r)->begin_resizing (at_front);
1966 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1968 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1969 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1970 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1972 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1977 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1979 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1980 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1981 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1983 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1988 NoteResizeDrag::aborted (bool)
1990 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1991 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1992 (*r)->abort_resizing ();
1996 AVDraggingView::AVDraggingView (RegionView* v)
1999 initial_position = v->region()->position ();
2002 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2005 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2008 TrackViewList empty;
2010 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2011 std::list<RegionView*> views = rs.by_layer();
2013 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2014 RegionView* rv = (*i);
2015 if (!rv->region()->video_locked()) {
2018 _views.push_back (AVDraggingView (rv));
2023 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2025 Drag::start_grab (event);
2026 if (_editor->session() == 0) {
2030 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2031 _max_backwards_drag = (
2032 ARDOUR_UI::instance()->video_timeline->get_duration()
2033 + ARDOUR_UI::instance()->video_timeline->get_offset()
2034 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2037 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2038 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2039 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2042 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2045 Timecode::Time timecode;
2046 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2047 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);
2048 show_verbose_cursor_text (buf);
2052 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2054 if (_editor->session() == 0) {
2057 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2061 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2062 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2064 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2065 dt = - _max_backwards_drag;
2068 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2069 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2071 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2072 RegionView* rv = i->view;
2073 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2076 rv->region()->clear_changes ();
2077 rv->region()->suspend_property_changes();
2079 rv->region()->set_position(i->initial_position + dt);
2080 rv->region_changed(ARDOUR::Properties::position);
2083 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2084 Timecode::Time timecode;
2085 Timecode::Time timediff;
2087 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2088 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2089 snprintf (buf, sizeof (buf),
2090 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2091 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2092 , _("Video Start:"),
2093 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2095 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2097 show_verbose_cursor_text (buf);
2101 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2103 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2107 if (!movement_occurred || ! _editor->session()) {
2111 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2113 _editor->begin_reversible_command (_("Move Video"));
2115 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2116 ARDOUR_UI::instance()->video_timeline->save_undo();
2117 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2118 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2120 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2121 i->view->drag_end();
2122 i->view->region()->resume_property_changes ();
2124 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2127 _editor->session()->maybe_update_session_range(
2128 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2129 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2133 _editor->commit_reversible_command ();
2137 VideoTimeLineDrag::aborted (bool)
2139 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2142 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2143 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2145 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2146 i->view->region()->resume_property_changes ();
2147 i->view->region()->set_position(i->initial_position);
2151 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2152 : RegionDrag (e, i, p, v)
2153 , _preserve_fade_anchor (preserve_fade_anchor)
2154 , _jump_position_when_done (false)
2156 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2160 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2163 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2164 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2166 if (tv && tv->is_track()) {
2167 speed = tv->track()->speed();
2170 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2171 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2172 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2174 framepos_t const pf = adjusted_current_frame (event);
2176 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2177 /* Move the contents of the region around without changing the region bounds */
2178 _operation = ContentsTrim;
2179 Drag::start_grab (event, _editor->cursors()->trimmer);
2181 /* These will get overridden for a point trim.*/
2182 if (pf < (region_start + region_length/2)) {
2183 /* closer to front */
2184 _operation = StartTrim;
2186 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2187 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2189 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2193 _operation = EndTrim;
2194 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2195 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2197 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2202 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2203 _jump_position_when_done = true;
2206 switch (_operation) {
2208 show_verbose_cursor_time (region_start);
2209 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2210 i->view->trim_front_starting ();
2214 show_verbose_cursor_time (region_end);
2217 show_verbose_cursor_time (pf);
2221 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2222 i->view->region()->suspend_property_changes ();
2227 TrimDrag::motion (GdkEvent* event, bool first_move)
2229 RegionView* rv = _primary;
2232 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2233 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2234 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2235 frameoffset_t frame_delta = 0;
2237 if (tv && tv->is_track()) {
2238 speed = tv->track()->speed();
2241 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2247 switch (_operation) {
2249 trim_type = "Region start trim";
2252 trim_type = "Region end trim";
2255 trim_type = "Region content trim";
2262 _editor->begin_reversible_command (trim_type);
2264 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2265 RegionView* rv = i->view;
2266 rv->enable_display (false);
2267 rv->region()->playlist()->clear_owned_changes ();
2269 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2272 arv->temporarily_hide_envelope ();
2276 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2277 insert_result = _editor->motion_frozen_playlists.insert (pl);
2279 if (insert_result.second) {
2285 bool non_overlap_trim = false;
2287 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2288 non_overlap_trim = true;
2291 /* contstrain trim to fade length */
2292 if (_preserve_fade_anchor) {
2293 switch (_operation) {
2295 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2296 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2298 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2299 if (ar->locked()) continue;
2300 framecnt_t len = ar->fade_in()->back()->when;
2301 if (len < dt) dt = min(dt, len);
2305 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2306 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2308 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2309 if (ar->locked()) continue;
2310 framecnt_t len = ar->fade_out()->back()->when;
2311 if (len < -dt) dt = max(dt, -len);
2320 switch (_operation) {
2322 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2323 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2324 if (changed && _preserve_fade_anchor) {
2325 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2327 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2328 framecnt_t len = ar->fade_in()->back()->when;
2329 framecnt_t diff = ar->first_frame() - i->initial_position;
2330 framepos_t new_length = len - diff;
2331 i->anchored_fade_length = min (ar->length(), new_length);
2332 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2333 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2340 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2341 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2342 if (changed && _preserve_fade_anchor) {
2343 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2345 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2346 framecnt_t len = ar->fade_out()->back()->when;
2347 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2348 framepos_t new_length = len + diff;
2349 i->anchored_fade_length = min (ar->length(), new_length);
2350 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2351 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2359 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2361 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2362 i->view->move_contents (frame_delta);
2368 switch (_operation) {
2370 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2373 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2376 // show_verbose_cursor_time (frame_delta);
2383 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2385 if (movement_occurred) {
2386 motion (event, false);
2388 if (_operation == StartTrim) {
2389 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2391 /* This must happen before the region's StatefulDiffCommand is created, as it may
2392 `correct' (ahem) the region's _start from being negative to being zero. It
2393 needs to be zero in the undo record.
2395 i->view->trim_front_ending ();
2397 if (_preserve_fade_anchor) {
2398 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2400 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2401 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2402 ar->set_fade_in_length(i->anchored_fade_length);
2403 ar->set_fade_in_active(true);
2406 if (_jump_position_when_done) {
2407 i->view->region()->set_position (i->initial_position);
2410 } else if (_operation == EndTrim) {
2411 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2412 if (_preserve_fade_anchor) {
2413 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2415 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2416 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2417 ar->set_fade_out_length(i->anchored_fade_length);
2418 ar->set_fade_out_active(true);
2421 if (_jump_position_when_done) {
2422 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2427 if (!_views.empty()) {
2428 if (_operation == StartTrim) {
2429 _editor->maybe_locate_with_edit_preroll(
2430 _views.begin()->view->region()->position());
2432 if (_operation == EndTrim) {
2433 _editor->maybe_locate_with_edit_preroll(
2434 _views.begin()->view->region()->position() +
2435 _views.begin()->view->region()->length());
2439 if (!_editor->selection->selected (_primary)) {
2440 _primary->thaw_after_trim ();
2443 set<boost::shared_ptr<Playlist> > diffed_playlists;
2445 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2446 i->view->thaw_after_trim ();
2447 i->view->enable_display (true);
2449 /* Trimming one region may affect others on the playlist, so we need
2450 to get undo Commands from the whole playlist rather than just the
2451 region. Use diffed_playlists to make sure we don't diff a given
2452 playlist more than once.
2454 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2455 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2456 vector<Command*> cmds;
2458 _editor->session()->add_commands (cmds);
2459 diffed_playlists.insert (p);
2464 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2468 _editor->motion_frozen_playlists.clear ();
2469 _editor->commit_reversible_command();
2472 /* no mouse movement */
2473 _editor->point_trim (event, adjusted_current_frame (event));
2476 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2477 if (_operation == StartTrim) {
2478 i->view->trim_front_ending ();
2481 i->view->region()->resume_property_changes ();
2486 TrimDrag::aborted (bool movement_occurred)
2488 /* Our motion method is changing model state, so use the Undo system
2489 to cancel. Perhaps not ideal, as this will leave an Undo point
2490 behind which may be slightly odd from the user's point of view.
2495 if (movement_occurred) {
2499 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2500 i->view->region()->resume_property_changes ();
2505 TrimDrag::setup_pointer_frame_offset ()
2507 list<DraggingView>::iterator i = _views.begin ();
2508 while (i != _views.end() && i->view != _primary) {
2512 if (i == _views.end()) {
2516 switch (_operation) {
2518 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2521 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2528 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2532 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2533 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2538 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2540 Drag::start_grab (event, cursor);
2541 show_verbose_cursor_time (adjusted_current_frame(event));
2545 MeterMarkerDrag::setup_pointer_frame_offset ()
2547 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2551 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2553 if (!_marker->meter().movable()) {
2559 // create a dummy marker for visual representation of moving the
2560 // section, because whether its a copy or not, we're going to
2561 // leave or lose the original marker (leave if its a copy; lose if its
2562 // not, because we'll remove it from the map).
2564 MeterSection section (_marker->meter());
2566 if (!section.movable()) {
2571 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2573 _marker = new MeterMarker (
2575 *_editor->meter_group,
2576 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2578 *new MeterSection (_marker->meter())
2581 /* use the new marker for the grab */
2582 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2585 TempoMap& map (_editor->session()->tempo_map());
2586 /* get current state */
2587 before_state = &map.get_state();
2588 /* remove the section while we drag it */
2589 map.remove_meter (section, true);
2593 framepos_t const pf = adjusted_current_frame (event);
2594 _marker->set_position (pf);
2595 show_verbose_cursor_time (pf);
2599 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2601 if (!movement_occurred) {
2602 if (was_double_click()) {
2603 _editor->edit_meter_marker (*_marker);
2608 if (!_marker->meter().movable()) {
2612 motion (event, false);
2614 Timecode::BBT_Time when;
2616 TempoMap& map (_editor->session()->tempo_map());
2617 map.bbt_time (last_pointer_frame(), when);
2619 if (_copy == true) {
2620 _editor->begin_reversible_command (_("copy meter mark"));
2621 XMLNode &before = map.get_state();
2622 map.add_meter (_marker->meter(), when);
2623 XMLNode &after = map.get_state();
2624 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2625 _editor->commit_reversible_command ();
2628 _editor->begin_reversible_command (_("move meter mark"));
2630 /* we removed it before, so add it back now */
2632 map.add_meter (_marker->meter(), when);
2633 XMLNode &after = map.get_state();
2634 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2635 _editor->commit_reversible_command ();
2638 // delete the dummy marker we used for visual representation while moving.
2639 // a new visual marker will show up automatically.
2644 MeterMarkerDrag::aborted (bool moved)
2646 _marker->set_position (_marker->meter().frame ());
2649 TempoMap& map (_editor->session()->tempo_map());
2650 /* we removed it before, so add it back now */
2651 map.add_meter (_marker->meter(), _marker->meter().frame());
2652 // delete the dummy marker we used for visual representation while moving.
2653 // a new visual marker will show up automatically.
2658 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2662 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2664 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2669 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2671 Drag::start_grab (event, cursor);
2672 show_verbose_cursor_time (adjusted_current_frame (event));
2676 TempoMarkerDrag::setup_pointer_frame_offset ()
2678 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2682 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2684 if (!_marker->tempo().movable()) {
2690 // create a dummy marker for visual representation of moving the
2691 // section, because whether its a copy or not, we're going to
2692 // leave or lose the original marker (leave if its a copy; lose if its
2693 // not, because we'll remove it from the map).
2695 // create a dummy marker for visual representation of moving the copy.
2696 // The actual copying is not done before we reach the finish callback.
2699 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2701 TempoSection section (_marker->tempo());
2703 _marker = new TempoMarker (
2705 *_editor->tempo_group,
2706 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2708 *new TempoSection (_marker->tempo())
2711 /* use the new marker for the grab */
2712 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2715 TempoMap& map (_editor->session()->tempo_map());
2716 /* get current state */
2717 before_state = &map.get_state();
2718 /* remove the section while we drag it */
2719 map.remove_tempo (section, true);
2723 framepos_t const pf = adjusted_current_frame (event);
2724 _marker->set_position (pf);
2725 show_verbose_cursor_time (pf);
2729 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2731 if (!movement_occurred) {
2732 if (was_double_click()) {
2733 _editor->edit_tempo_marker (*_marker);
2738 if (!_marker->tempo().movable()) {
2742 motion (event, false);
2744 TempoMap& map (_editor->session()->tempo_map());
2745 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2746 Timecode::BBT_Time when;
2748 map.bbt_time (beat_time, when);
2750 if (_copy == true) {
2751 _editor->begin_reversible_command (_("copy tempo mark"));
2752 XMLNode &before = map.get_state();
2753 map.add_tempo (_marker->tempo(), when);
2754 XMLNode &after = map.get_state();
2755 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2756 _editor->commit_reversible_command ();
2759 _editor->begin_reversible_command (_("move tempo mark"));
2760 /* we removed it before, so add it back now */
2761 map.add_tempo (_marker->tempo(), when);
2762 XMLNode &after = map.get_state();
2763 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2764 _editor->commit_reversible_command ();
2767 // delete the dummy marker we used for visual representation while moving.
2768 // a new visual marker will show up automatically.
2773 TempoMarkerDrag::aborted (bool moved)
2775 _marker->set_position (_marker->tempo().frame());
2777 TempoMap& map (_editor->session()->tempo_map());
2778 /* we removed it before, so add it back now */
2779 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2780 // delete the dummy marker we used for visual representation while moving.
2781 // a new visual marker will show up automatically.
2786 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2787 : Drag (e, &c.track_canvas_item(), false)
2791 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2794 /** Do all the things we do when dragging the playhead to make it look as though
2795 * we have located, without actually doing the locate (because that would cause
2796 * the diskstream buffers to be refilled, which is too slow).
2799 CursorDrag::fake_locate (framepos_t t)
2801 _editor->playhead_cursor->set_position (t);
2803 Session* s = _editor->session ();
2804 if (s->timecode_transmission_suspended ()) {
2805 framepos_t const f = _editor->playhead_cursor->current_frame ();
2806 /* This is asynchronous so it will be sent "now"
2808 s->send_mmc_locate (f);
2809 /* These are synchronous and will be sent during the next
2812 s->queue_full_time_code ();
2813 s->queue_song_position_pointer ();
2816 show_verbose_cursor_time (t);
2817 _editor->UpdateAllTransportClocks (t);
2821 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2823 Drag::start_grab (event, c);
2825 _grab_zoom = _editor->samples_per_pixel;
2827 framepos_t where = _editor->canvas_event_sample (event);
2829 _editor->snap_to_with_modifier (where, event);
2831 _editor->_dragging_playhead = true;
2833 Session* s = _editor->session ();
2835 /* grab the track canvas item as well */
2837 _cursor.track_canvas_item().grab();
2840 if (_was_rolling && _stop) {
2844 if (s->is_auditioning()) {
2845 s->cancel_audition ();
2849 if (AudioEngine::instance()->connected()) {
2851 /* do this only if we're the engine is connected
2852 * because otherwise this request will never be
2853 * serviced and we'll busy wait forever. likewise,
2854 * notice if we are disconnected while waiting for the
2855 * request to be serviced.
2858 s->request_suspend_timecode_transmission ();
2859 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2860 /* twiddle our thumbs */
2865 fake_locate (where);
2869 CursorDrag::motion (GdkEvent* event, bool)
2871 framepos_t const adjusted_frame = adjusted_current_frame (event);
2872 if (adjusted_frame != last_pointer_frame()) {
2873 fake_locate (adjusted_frame);
2878 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2880 _editor->_dragging_playhead = false;
2882 _cursor.track_canvas_item().ungrab();
2884 if (!movement_occurred && _stop) {
2888 motion (event, false);
2890 Session* s = _editor->session ();
2892 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2893 _editor->_pending_locate_request = true;
2894 s->request_resume_timecode_transmission ();
2899 CursorDrag::aborted (bool)
2901 _cursor.track_canvas_item().ungrab();
2903 if (_editor->_dragging_playhead) {
2904 _editor->session()->request_resume_timecode_transmission ();
2905 _editor->_dragging_playhead = false;
2908 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2911 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2912 : RegionDrag (e, i, p, v)
2914 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2918 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2920 Drag::start_grab (event, cursor);
2922 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2923 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2925 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2929 FadeInDrag::setup_pointer_frame_offset ()
2931 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2932 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2933 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2937 FadeInDrag::motion (GdkEvent* event, bool)
2939 framecnt_t fade_length;
2941 framepos_t const pos = adjusted_current_frame (event);
2943 boost::shared_ptr<Region> region = _primary->region ();
2945 if (pos < (region->position() + 64)) {
2946 fade_length = 64; // this should be a minimum defined somewhere
2947 } else if (pos > region->last_frame()) {
2948 fade_length = region->length();
2950 fade_length = pos - region->position();
2953 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2955 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2961 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2964 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2968 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2970 if (!movement_occurred) {
2974 framecnt_t fade_length;
2976 framepos_t const pos = adjusted_current_frame (event);
2978 boost::shared_ptr<Region> region = _primary->region ();
2980 if (pos < (region->position() + 64)) {
2981 fade_length = 64; // this should be a minimum defined somewhere
2982 } else if (pos > region->last_frame()) {
2983 fade_length = region->length();
2985 fade_length = pos - region->position();
2988 _editor->begin_reversible_command (_("change fade in length"));
2990 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2992 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2998 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2999 XMLNode &before = alist->get_state();
3001 tmp->audio_region()->set_fade_in_length (fade_length);
3002 tmp->audio_region()->set_fade_in_active (true);
3004 XMLNode &after = alist->get_state();
3005 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3008 _editor->commit_reversible_command ();
3012 FadeInDrag::aborted (bool)
3014 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3015 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3021 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3025 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3026 : RegionDrag (e, i, p, v)
3028 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3032 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3034 Drag::start_grab (event, cursor);
3036 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3037 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3039 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3043 FadeOutDrag::setup_pointer_frame_offset ()
3045 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3046 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3047 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3051 FadeOutDrag::motion (GdkEvent* event, bool)
3053 framecnt_t fade_length;
3055 framepos_t const pos = adjusted_current_frame (event);
3057 boost::shared_ptr<Region> region = _primary->region ();
3059 if (pos > (region->last_frame() - 64)) {
3060 fade_length = 64; // this should really be a minimum fade defined somewhere
3062 else if (pos < region->position()) {
3063 fade_length = region->length();
3066 fade_length = region->last_frame() - pos;
3069 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3071 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3077 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3080 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3084 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3086 if (!movement_occurred) {
3090 framecnt_t fade_length;
3092 framepos_t const pos = adjusted_current_frame (event);
3094 boost::shared_ptr<Region> region = _primary->region ();
3096 if (pos > (region->last_frame() - 64)) {
3097 fade_length = 64; // this should really be a minimum fade defined somewhere
3099 else if (pos < region->position()) {
3100 fade_length = region->length();
3103 fade_length = region->last_frame() - pos;
3106 _editor->begin_reversible_command (_("change fade out length"));
3108 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3110 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3116 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3117 XMLNode &before = alist->get_state();
3119 tmp->audio_region()->set_fade_out_length (fade_length);
3120 tmp->audio_region()->set_fade_out_active (true);
3122 XMLNode &after = alist->get_state();
3123 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3126 _editor->commit_reversible_command ();
3130 FadeOutDrag::aborted (bool)
3132 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3133 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3139 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3143 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3146 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3148 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3151 _points.push_back (ArdourCanvas::Duple (0, 0));
3152 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3155 MarkerDrag::~MarkerDrag ()
3157 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3162 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3164 location = new Location (*l);
3165 markers.push_back (m);
3170 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3172 Drag::start_grab (event, cursor);
3176 Location *location = _editor->find_location_from_marker (_marker, is_start);
3177 _editor->_dragging_edit_point = true;
3179 update_item (location);
3181 // _drag_line->show();
3182 // _line->raise_to_top();
3185 show_verbose_cursor_time (location->start());
3187 show_verbose_cursor_time (location->end());
3190 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3193 case Selection::Toggle:
3194 /* we toggle on the button release */
3196 case Selection::Set:
3197 if (!_editor->selection->selected (_marker)) {
3198 _editor->selection->set (_marker);
3201 case Selection::Extend:
3203 Locations::LocationList ll;
3204 list<Marker*> to_add;
3206 _editor->selection->markers.range (s, e);
3207 s = min (_marker->position(), s);
3208 e = max (_marker->position(), e);
3211 if (e < max_framepos) {
3214 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3215 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3216 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3219 to_add.push_back (lm->start);
3222 to_add.push_back (lm->end);
3226 if (!to_add.empty()) {
3227 _editor->selection->add (to_add);
3231 case Selection::Add:
3232 _editor->selection->add (_marker);
3236 /* Set up copies for us to manipulate during the drag
3239 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3241 Location* l = _editor->find_location_from_marker (*i, is_start);
3248 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3250 /* range: check that the other end of the range isn't
3253 CopiedLocationInfo::iterator x;
3254 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3255 if (*(*x).location == *l) {
3259 if (x == _copied_locations.end()) {
3260 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3262 (*x).markers.push_back (*i);
3263 (*x).move_both = true;
3271 MarkerDrag::setup_pointer_frame_offset ()
3274 Location *location = _editor->find_location_from_marker (_marker, is_start);
3275 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3279 MarkerDrag::motion (GdkEvent* event, bool)
3281 framecnt_t f_delta = 0;
3283 bool move_both = false;
3284 Location *real_location;
3285 Location *copy_location = 0;
3287 framepos_t const newframe = adjusted_current_frame (event);
3288 framepos_t next = newframe;
3290 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3294 CopiedLocationInfo::iterator x;
3296 /* find the marker we're dragging, and compute the delta */
3298 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3300 copy_location = (*x).location;
3302 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3304 /* this marker is represented by this
3305 * CopiedLocationMarkerInfo
3308 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3313 if (real_location->is_mark()) {
3314 f_delta = newframe - copy_location->start();
3318 switch (_marker->type()) {
3319 case Marker::SessionStart:
3320 case Marker::RangeStart:
3321 case Marker::LoopStart:
3322 case Marker::PunchIn:
3323 f_delta = newframe - copy_location->start();
3326 case Marker::SessionEnd:
3327 case Marker::RangeEnd:
3328 case Marker::LoopEnd:
3329 case Marker::PunchOut:
3330 f_delta = newframe - copy_location->end();
3333 /* what kind of marker is this ? */
3342 if (x == _copied_locations.end()) {
3343 /* hmm, impossible - we didn't find the dragged marker */
3347 /* now move them all */
3349 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3351 copy_location = x->location;
3353 /* call this to find out if its the start or end */
3355 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3359 if (real_location->locked()) {
3363 if (copy_location->is_mark()) {
3367 copy_location->set_start (copy_location->start() + f_delta);
3371 framepos_t new_start = copy_location->start() + f_delta;
3372 framepos_t new_end = copy_location->end() + f_delta;
3374 if (is_start) { // start-of-range marker
3376 if (move_both || (*x).move_both) {
3377 copy_location->set_start (new_start);
3378 copy_location->set_end (new_end);
3379 } else if (new_start < copy_location->end()) {
3380 copy_location->set_start (new_start);
3381 } else if (newframe > 0) {
3382 _editor->snap_to (next, 1, true);
3383 copy_location->set_end (next);
3384 copy_location->set_start (newframe);
3387 } else { // end marker
3389 if (move_both || (*x).move_both) {
3390 copy_location->set_end (new_end);
3391 copy_location->set_start (new_start);
3392 } else if (new_end > copy_location->start()) {
3393 copy_location->set_end (new_end);
3394 } else if (newframe > 0) {
3395 _editor->snap_to (next, -1, true);
3396 copy_location->set_start (next);
3397 copy_location->set_end (newframe);
3402 update_item (copy_location);
3404 /* now lookup the actual GUI items used to display this
3405 * location and move them to wherever the copy of the location
3406 * is now. This means that the logic in ARDOUR::Location is
3407 * still enforced, even though we are not (yet) modifying
3408 * the real Location itself.
3411 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3414 lm->set_position (copy_location->start(), copy_location->end());
3419 assert (!_copied_locations.empty());
3421 show_verbose_cursor_time (newframe);
3425 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3427 if (!movement_occurred) {
3429 if (was_double_click()) {
3430 _editor->rename_marker (_marker);
3434 /* just a click, do nothing but finish
3435 off the selection process
3438 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3441 case Selection::Set:
3442 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3443 _editor->selection->set (_marker);
3447 case Selection::Toggle:
3448 /* we toggle on the button release, click only */
3449 _editor->selection->toggle (_marker);
3452 case Selection::Extend:
3453 case Selection::Add:
3460 _editor->_dragging_edit_point = false;
3462 _editor->begin_reversible_command ( _("move marker") );
3463 XMLNode &before = _editor->session()->locations()->get_state();
3465 MarkerSelection::iterator i;
3466 CopiedLocationInfo::iterator x;
3469 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3470 x != _copied_locations.end() && i != _editor->selection->markers.end();
3473 Location * location = _editor->find_location_from_marker (*i, is_start);
3477 if (location->locked()) {
3481 if (location->is_mark()) {
3482 location->set_start (((*x).location)->start());
3484 location->set (((*x).location)->start(), ((*x).location)->end());
3489 XMLNode &after = _editor->session()->locations()->get_state();
3490 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3491 _editor->commit_reversible_command ();
3495 MarkerDrag::aborted (bool)
3501 MarkerDrag::update_item (Location*)
3506 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3508 _cumulative_x_drag (0),
3509 _cumulative_y_drag (0)
3511 if (_zero_gain_fraction < 0.0) {
3512 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3515 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3517 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3523 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3525 Drag::start_grab (event, _editor->cursors()->fader);
3527 // start the grab at the center of the control point so
3528 // the point doesn't 'jump' to the mouse after the first drag
3529 _fixed_grab_x = _point->get_x();
3530 _fixed_grab_y = _point->get_y();
3532 float const fraction = 1 - (_point->get_y() / _point->line().height());
3534 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3536 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3538 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3540 if (!_point->can_slide ()) {
3541 _x_constrained = true;
3546 ControlPointDrag::motion (GdkEvent* event, bool)
3548 double dx = _drags->current_pointer_x() - last_pointer_x();
3549 double dy = current_pointer_y() - last_pointer_y();
3551 if (event->button.state & Keyboard::SecondaryModifier) {
3556 /* coordinate in pixels relative to the start of the region (for region-based automation)
3557 or track (for track-based automation) */
3558 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3559 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3561 // calculate zero crossing point. back off by .01 to stay on the
3562 // positive side of zero
3563 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3565 // make sure we hit zero when passing through
3566 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3570 if (_x_constrained) {
3573 if (_y_constrained) {
3577 _cumulative_x_drag = cx - _fixed_grab_x;
3578 _cumulative_y_drag = cy - _fixed_grab_y;
3582 cy = min ((double) _point->line().height(), cy);
3584 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3586 if (!_x_constrained) {
3587 _editor->snap_to_with_modifier (cx_frames, event);
3590 cx_frames = min (cx_frames, _point->line().maximum_time());
3592 float const fraction = 1.0 - (cy / _point->line().height());
3594 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3596 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3600 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3602 if (!movement_occurred) {
3606 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3607 _editor->reset_point_selection ();
3611 motion (event, false);
3614 _point->line().end_drag (_pushing, _final_index);
3615 _editor->session()->commit_reversible_command ();
3619 ControlPointDrag::aborted (bool)
3621 _point->line().reset ();
3625 ControlPointDrag::active (Editing::MouseMode m)
3627 if (m == Editing::MouseGain) {
3628 /* always active in mouse gain */
3632 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3633 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3636 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3639 _cumulative_y_drag (0)
3641 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3645 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3647 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3650 _item = &_line->grab_item ();
3652 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3653 origin, and ditto for y.
3656 double cx = event->button.x;
3657 double cy = event->button.y;
3659 _line->parent_group().canvas_to_item (cx, cy);
3661 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3666 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3667 /* no adjacent points */
3671 Drag::start_grab (event, _editor->cursors()->fader);
3673 /* store grab start in parent frame */
3678 double fraction = 1.0 - (cy / _line->height());
3680 _line->start_drag_line (before, after, fraction);
3682 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3686 LineDrag::motion (GdkEvent* event, bool)
3688 double dy = current_pointer_y() - last_pointer_y();
3690 if (event->button.state & Keyboard::SecondaryModifier) {
3694 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3696 _cumulative_y_drag = cy - _fixed_grab_y;
3699 cy = min ((double) _line->height(), cy);
3701 double const fraction = 1.0 - (cy / _line->height());
3704 /* we are ignoring x position for this drag, so we can just pass in anything */
3705 _line->drag_motion (0, fraction, true, false, ignored);
3707 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3711 LineDrag::finished (GdkEvent* event, bool movement_occured)
3713 if (movement_occured) {
3714 motion (event, false);
3715 _line->end_drag (false, 0);
3717 /* add a new control point on the line */
3719 AutomationTimeAxisView* atv;
3721 _line->end_drag (false, 0);
3723 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3724 framepos_t where = _editor->window_event_sample (event, 0, 0);
3725 atv->add_automation_event (event, where, event->button.y, false);
3729 _editor->session()->commit_reversible_command ();
3733 LineDrag::aborted (bool)
3738 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3741 _cumulative_x_drag (0)
3743 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3747 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3749 Drag::start_grab (event);
3751 _line = reinterpret_cast<Line*> (_item);
3754 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3756 double cx = event->button.x;
3757 double cy = event->button.y;
3759 _item->parent()->canvas_to_item (cx, cy);
3761 /* store grab start in parent frame */
3762 _region_view_grab_x = cx;
3764 _before = *(float*) _item->get_data ("position");
3766 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3768 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3772 FeatureLineDrag::motion (GdkEvent*, bool)
3774 double dx = _drags->current_pointer_x() - last_pointer_x();
3776 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3778 _cumulative_x_drag += dx;
3780 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3789 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3791 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3793 float *pos = new float;
3796 _line->set_data ("position", pos);
3802 FeatureLineDrag::finished (GdkEvent*, bool)
3804 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3805 _arv->update_transient(_before, _before);
3809 FeatureLineDrag::aborted (bool)
3814 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3816 , _vertical_only (false)
3818 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3822 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3824 Drag::start_grab (event);
3825 show_verbose_cursor_time (adjusted_current_frame (event));
3829 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3836 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3838 framepos_t grab = grab_frame ();
3839 if (Config->get_rubberbanding_snaps_to_grid ()) {
3840 _editor->snap_to_with_modifier (grab, event);
3843 /* base start and end on initial click position */
3853 if (current_pointer_y() < grab_y()) {
3854 y1 = current_pointer_y();
3857 y2 = current_pointer_y();
3861 if (start != end || y1 != y2) {
3863 double x1 = _editor->sample_to_pixel (start);
3864 double x2 = _editor->sample_to_pixel (end);
3865 const double min_dimension = 2.0;
3867 if (_vertical_only) {
3868 /* fixed 10 pixel width */
3872 x2 = min (x1 - min_dimension, x2);
3874 x2 = max (x1 + min_dimension, x2);
3879 y2 = min (y1 - min_dimension, y2);
3881 y2 = max (y1 + min_dimension, y2);
3884 /* translate rect into item space and set */
3886 ArdourCanvas::Rect r (x1, y1, x2, y2);
3888 /* this drag is a _trackview_only == true drag, so the y1 and
3889 * y2 (computed using current_pointer_y() and grab_y()) will be
3890 * relative to the top of the trackview group). The
3891 * rubberband rect has the same parent/scroll offset as the
3892 * the trackview group, so we can use the "r" rect directly
3893 * to set the shape of the rubberband.
3896 _editor->rubberband_rect->set (r);
3897 _editor->rubberband_rect->show();
3898 _editor->rubberband_rect->raise_to_top();
3900 show_verbose_cursor_time (pf);
3902 do_select_things (event, true);
3907 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3912 if (grab_frame() < last_pointer_frame()) {
3914 x2 = last_pointer_frame ();
3917 x1 = last_pointer_frame ();
3923 if (current_pointer_y() < grab_y()) {
3924 y1 = current_pointer_y();
3927 y2 = current_pointer_y();
3931 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3935 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3937 if (movement_occurred) {
3939 motion (event, false);
3940 do_select_things (event, false);
3946 bool do_deselect = true;
3947 MidiTimeAxisView* mtv;
3949 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3951 if (_editor->selection->empty()) {
3952 /* nothing selected */
3953 add_midi_region (mtv);
3954 do_deselect = false;
3958 /* do not deselect if Primary or Tertiary (toggle-select or
3959 * extend-select are pressed.
3962 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3963 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3970 _editor->rubberband_rect->hide();
3974 RubberbandSelectDrag::aborted (bool)
3976 _editor->rubberband_rect->hide ();
3979 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3980 : RegionDrag (e, i, p, v)
3982 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3986 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3988 Drag::start_grab (event, cursor);
3990 show_verbose_cursor_time (adjusted_current_frame (event));
3994 TimeFXDrag::motion (GdkEvent* event, bool)
3996 RegionView* rv = _primary;
3997 StreamView* cv = rv->get_time_axis_view().view ();
3999 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4000 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4001 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4003 framepos_t const pf = adjusted_current_frame (event);
4005 if (pf > rv->region()->position()) {
4006 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4009 show_verbose_cursor_time (pf);
4013 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4015 _primary->get_time_axis_view().hide_timestretch ();
4017 if (!movement_occurred) {
4021 if (last_pointer_frame() < _primary->region()->position()) {
4022 /* backwards drag of the left edge - not usable */
4026 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4028 float percentage = (double) newlen / (double) _primary->region()->length();
4030 #ifndef USE_RUBBERBAND
4031 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4032 if (_primary->region()->data_type() == DataType::AUDIO) {
4033 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4037 if (!_editor->get_selection().regions.empty()) {
4038 /* primary will already be included in the selection, and edit
4039 group shared editing will propagate selection across
4040 equivalent regions, so just use the current region
4044 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4045 error << _("An error occurred while executing time stretch operation") << endmsg;
4051 TimeFXDrag::aborted (bool)
4053 _primary->get_time_axis_view().hide_timestretch ();
4056 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4059 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4063 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4065 Drag::start_grab (event);
4069 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4071 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4075 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4077 if (movement_occurred && _editor->session()) {
4078 /* make sure we stop */
4079 _editor->session()->request_transport_speed (0.0);
4084 ScrubDrag::aborted (bool)
4089 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4094 , _original_pointer_time_axis (-1)
4095 , _last_pointer_time_axis (-1)
4096 , _time_selection_at_start (!_editor->get_selection().time.empty())
4098 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4100 if (_time_selection_at_start) {
4101 start_at_start = _editor->get_selection().time.start();
4102 end_at_start = _editor->get_selection().time.end_frame();
4107 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4109 if (_editor->session() == 0) {
4113 Gdk::Cursor* cursor = 0;
4115 switch (_operation) {
4116 case CreateSelection:
4117 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4122 cursor = _editor->cursors()->selector;
4123 Drag::start_grab (event, cursor);
4126 case SelectionStartTrim:
4127 if (_editor->clicked_axisview) {
4128 _editor->clicked_axisview->order_selection_trims (_item, true);
4130 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4133 case SelectionEndTrim:
4134 if (_editor->clicked_axisview) {
4135 _editor->clicked_axisview->order_selection_trims (_item, false);
4137 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4141 Drag::start_grab (event, cursor);
4144 case SelectionExtend:
4145 Drag::start_grab (event, cursor);
4149 if (_operation == SelectionMove) {
4150 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4152 show_verbose_cursor_time (adjusted_current_frame (event));
4155 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4159 SelectionDrag::setup_pointer_frame_offset ()
4161 switch (_operation) {
4162 case CreateSelection:
4163 _pointer_frame_offset = 0;
4166 case SelectionStartTrim:
4168 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4171 case SelectionEndTrim:
4172 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4175 case SelectionExtend:
4181 SelectionDrag::motion (GdkEvent* event, bool first_move)
4183 framepos_t start = 0;
4185 framecnt_t length = 0;
4186 framecnt_t distance = 0;
4188 framepos_t const pending_position = adjusted_current_frame (event);
4190 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4194 switch (_operation) {
4195 case CreateSelection:
4197 framepos_t grab = grab_frame ();
4200 grab = adjusted_current_frame (event, false);
4201 if (grab < pending_position) {
4202 _editor->snap_to (grab, -1);
4204 _editor->snap_to (grab, 1);
4208 if (pending_position < grab) {
4209 start = pending_position;
4212 end = pending_position;
4216 /* first drag: Either add to the selection
4217 or create a new selection
4224 /* adding to the selection */
4225 _editor->set_selected_track_as_side_effect (Selection::Add);
4226 _editor->clicked_selection = _editor->selection->add (start, end);
4233 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4234 _editor->set_selected_track_as_side_effect (Selection::Set);
4237 _editor->clicked_selection = _editor->selection->set (start, end);
4241 /* select all tracks within the rectangle that we've marked out so far */
4242 TrackViewList to_be_added_to_selection;
4243 TrackViewList to_be_removed_from_selection;
4244 TrackViewList& all_tracks (_editor->track_views);
4246 ArdourCanvas::Coord const top = grab_y();
4247 ArdourCanvas::Coord const bottom = current_pointer_y();
4249 if (top >= 0 && bottom >= 0) {
4251 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4253 if ((*i)->covered_by_y_range (top, bottom)) {
4254 if (!(*i)->get_selected()) {
4255 to_be_added_to_selection.push_back (*i);
4258 if ((*i)->get_selected()) {
4259 to_be_removed_from_selection.push_back (*i);
4264 if (!to_be_added_to_selection.empty()) {
4265 _editor->selection->add (to_be_added_to_selection);
4268 if (!to_be_removed_from_selection.empty()) {
4269 _editor->selection->remove (to_be_removed_from_selection);
4275 case SelectionStartTrim:
4277 start = _editor->selection->time[_editor->clicked_selection].start;
4278 end = _editor->selection->time[_editor->clicked_selection].end;
4280 if (pending_position > end) {
4283 start = pending_position;
4287 case SelectionEndTrim:
4289 start = _editor->selection->time[_editor->clicked_selection].start;
4290 end = _editor->selection->time[_editor->clicked_selection].end;
4292 if (pending_position < start) {
4295 end = pending_position;
4302 start = _editor->selection->time[_editor->clicked_selection].start;
4303 end = _editor->selection->time[_editor->clicked_selection].end;
4305 length = end - start;
4306 distance = pending_position - start;
4307 start = pending_position;
4308 _editor->snap_to (start);
4310 end = start + length;
4314 case SelectionExtend:
4319 switch (_operation) {
4321 if (_time_selection_at_start) {
4322 _editor->selection->move_time (distance);
4326 _editor->selection->replace (_editor->clicked_selection, start, end);
4330 if (_operation == SelectionMove) {
4331 show_verbose_cursor_time(start);
4333 show_verbose_cursor_time(pending_position);
4338 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4340 Session* s = _editor->session();
4342 if (movement_occurred) {
4343 motion (event, false);
4344 /* XXX this is not object-oriented programming at all. ick */
4345 if (_editor->selection->time.consolidate()) {
4346 _editor->selection->TimeChanged ();
4349 /* XXX what if its a music time selection? */
4351 if ( s->get_play_range() && s->transport_rolling() ) {
4352 s->request_play_range (&_editor->selection->time, true);
4354 if (Config->get_follow_edits() && !s->transport_rolling()) {
4355 if (_operation == SelectionEndTrim)
4356 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4358 s->request_locate (_editor->get_selection().time.start());
4364 /* just a click, no pointer movement.
4367 if (_operation == SelectionExtend) {
4368 if (_time_selection_at_start) {
4369 framepos_t pos = adjusted_current_frame (event, false);
4370 framepos_t start = min (pos, start_at_start);
4371 framepos_t end = max (pos, end_at_start);
4372 _editor->selection->set (start, end);
4375 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4376 if (_editor->clicked_selection) {
4377 _editor->selection->remove (_editor->clicked_selection);
4380 if (!_editor->clicked_selection) {
4381 _editor->selection->clear_time();
4386 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4387 _editor->selection->set (_editor->clicked_axisview);
4390 if (s && s->get_play_range () && s->transport_rolling()) {
4391 s->request_stop (false, false);
4396 _editor->stop_canvas_autoscroll ();
4397 _editor->clicked_selection = 0;
4401 SelectionDrag::aborted (bool)
4406 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4407 : Drag (e, i, false),
4411 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4413 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4414 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4415 physical_screen_height (_editor->get_window())));
4416 _drag_rect->hide ();
4418 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4419 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4423 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4425 if (_editor->session() == 0) {
4429 Gdk::Cursor* cursor = 0;
4431 if (!_editor->temp_location) {
4432 _editor->temp_location = new Location (*_editor->session());
4435 switch (_operation) {
4436 case CreateRangeMarker:
4437 case CreateTransportMarker:
4438 case CreateCDMarker:
4440 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4445 cursor = _editor->cursors()->selector;
4449 Drag::start_grab (event, cursor);
4451 show_verbose_cursor_time (adjusted_current_frame (event));
4455 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4457 framepos_t start = 0;
4459 ArdourCanvas::Rectangle *crect;
4461 switch (_operation) {
4462 case CreateRangeMarker:
4463 crect = _editor->range_bar_drag_rect;
4465 case CreateTransportMarker:
4466 crect = _editor->transport_bar_drag_rect;
4468 case CreateCDMarker:
4469 crect = _editor->cd_marker_bar_drag_rect;
4472 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4477 framepos_t const pf = adjusted_current_frame (event);
4479 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4480 framepos_t grab = grab_frame ();
4481 _editor->snap_to (grab);
4483 if (pf < grab_frame()) {
4491 /* first drag: Either add to the selection
4492 or create a new selection.
4497 _editor->temp_location->set (start, end);
4501 update_item (_editor->temp_location);
4503 //_drag_rect->raise_to_top();
4509 _editor->temp_location->set (start, end);
4511 double x1 = _editor->sample_to_pixel (start);
4512 double x2 = _editor->sample_to_pixel (end);
4516 update_item (_editor->temp_location);
4519 show_verbose_cursor_time (pf);
4524 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4526 Location * newloc = 0;
4530 if (movement_occurred) {
4531 motion (event, false);
4534 switch (_operation) {
4535 case CreateRangeMarker:
4536 case CreateCDMarker:
4538 _editor->begin_reversible_command (_("new range marker"));
4539 XMLNode &before = _editor->session()->locations()->get_state();
4540 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4541 if (_operation == CreateCDMarker) {
4542 flags = Location::IsRangeMarker | Location::IsCDMarker;
4543 _editor->cd_marker_bar_drag_rect->hide();
4546 flags = Location::IsRangeMarker;
4547 _editor->range_bar_drag_rect->hide();
4549 newloc = new Location (
4550 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4553 _editor->session()->locations()->add (newloc, true);
4554 XMLNode &after = _editor->session()->locations()->get_state();
4555 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4556 _editor->commit_reversible_command ();
4560 case CreateTransportMarker:
4561 // popup menu to pick loop or punch
4562 _editor->new_transport_marker_context_menu (&event->button, _item);
4568 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4570 if (_operation == CreateTransportMarker) {
4572 /* didn't drag, so just locate */
4574 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4576 } else if (_operation == CreateCDMarker) {
4578 /* didn't drag, but mark is already created so do
4581 } else { /* operation == CreateRangeMarker */
4587 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4589 if (end == max_framepos) {
4590 end = _editor->session()->current_end_frame ();
4593 if (start == max_framepos) {
4594 start = _editor->session()->current_start_frame ();
4597 switch (_editor->mouse_mode) {
4599 /* find the two markers on either side and then make the selection from it */
4600 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4604 /* find the two markers on either side of the click and make the range out of it */
4605 _editor->selection->set (start, end);
4614 _editor->stop_canvas_autoscroll ();
4618 RangeMarkerBarDrag::aborted (bool)
4624 RangeMarkerBarDrag::update_item (Location* location)
4626 double const x1 = _editor->sample_to_pixel (location->start());
4627 double const x2 = _editor->sample_to_pixel (location->end());
4629 _drag_rect->set_x0 (x1);
4630 _drag_rect->set_x1 (x2);
4633 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4637 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4641 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4643 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4644 Drag::start_grab (event, _editor->cursors()->zoom_out);
4647 Drag::start_grab (event, _editor->cursors()->zoom_in);
4651 show_verbose_cursor_time (adjusted_current_frame (event));
4655 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4660 framepos_t const pf = adjusted_current_frame (event);
4662 framepos_t grab = grab_frame ();
4663 _editor->snap_to_with_modifier (grab, event);
4665 /* base start and end on initial click position */
4677 _editor->zoom_rect->show();
4678 _editor->zoom_rect->raise_to_top();
4681 _editor->reposition_zoom_rect(start, end);
4683 show_verbose_cursor_time (pf);
4688 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4690 if (movement_occurred) {
4691 motion (event, false);
4693 if (grab_frame() < last_pointer_frame()) {
4694 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4696 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4699 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4700 _editor->tav_zoom_step (_zoom_out);
4702 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4706 _editor->zoom_rect->hide();
4710 MouseZoomDrag::aborted (bool)
4712 _editor->zoom_rect->hide ();
4715 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4717 , _cumulative_dx (0)
4718 , _cumulative_dy (0)
4720 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4722 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4724 _region = &_primary->region_view ();
4725 _note_height = _region->midi_stream_view()->note_height ();
4729 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4731 Drag::start_grab (event);
4733 if (!(_was_selected = _primary->selected())) {
4735 /* tertiary-click means extend selection - we'll do that on button release,
4736 so don't add it here, because otherwise we make it hard to figure
4737 out the "extend-to" range.
4740 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4743 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4746 _region->note_selected (_primary, true);
4748 _region->unique_select (_primary);
4754 /** @return Current total drag x change in frames */
4756 NoteDrag::total_dx () const
4759 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4761 /* primary note time */
4762 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4764 /* new time of the primary note in session frames */
4765 frameoffset_t st = n + dx;
4767 framepos_t const rp = _region->region()->position ();
4769 /* prevent the note being dragged earlier than the region's position */
4772 /* snap and return corresponding delta */
4773 return _region->snap_frame_to_frame (st - rp) + rp - n;
4776 /** @return Current total drag y change in note number */
4778 NoteDrag::total_dy () const
4780 MidiStreamView* msv = _region->midi_stream_view ();
4781 double const y = _region->midi_view()->y_position ();
4782 /* new current note */
4783 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4785 n = max (msv->lowest_note(), n);
4786 n = min (msv->highest_note(), n);
4787 /* and work out delta */
4788 return n - msv->y_to_note (grab_y() - y);
4792 NoteDrag::motion (GdkEvent *, bool)
4794 /* Total change in x and y since the start of the drag */
4795 frameoffset_t const dx = total_dx ();
4796 int8_t const dy = total_dy ();
4798 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4799 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4800 double const tdy = -dy * _note_height - _cumulative_dy;
4803 _cumulative_dx += tdx;
4804 _cumulative_dy += tdy;
4806 int8_t note_delta = total_dy();
4808 _region->move_selection (tdx, tdy, note_delta);
4810 /* the new note value may be the same as the old one, but we
4811 * don't know what that means because the selection may have
4812 * involved more than one note and we might be doing something
4813 * odd with them. so show the note value anyway, always.
4817 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4819 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4820 (int) floor ((double)new_note));
4822 show_verbose_cursor_text (buf);
4827 NoteDrag::finished (GdkEvent* ev, bool moved)
4830 /* no motion - select note */
4832 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4833 _editor->current_mouse_mode() == Editing::MouseDraw) {
4835 if (_was_selected) {
4836 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4838 _region->note_deselected (_primary);
4841 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4842 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4844 if (!extend && !add && _region->selection_size() > 1) {
4845 _region->unique_select (_primary);
4846 } else if (extend) {
4847 _region->note_selected (_primary, true, true);
4849 /* it was added during button press */
4854 _region->note_dropped (_primary, total_dx(), total_dy());
4859 NoteDrag::aborted (bool)
4864 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4865 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4866 : Drag (editor, atv->base_item ())
4868 , _nothing_to_drag (false)
4870 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4871 y_origin = atv->y_position();
4872 setup (atv->lines ());
4875 /** Make an AutomationRangeDrag for region gain lines */
4876 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4877 : Drag (editor, rv->get_canvas_group ())
4879 , _nothing_to_drag (false)
4881 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4883 list<boost::shared_ptr<AutomationLine> > lines;
4884 lines.push_back (rv->get_gain_line ());
4885 y_origin = rv->get_time_axis_view().y_position();
4889 /** @param lines AutomationLines to drag.
4890 * @param offset Offset from the session start to the points in the AutomationLines.
4893 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4895 /* find the lines that overlap the ranges being dragged */
4896 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4897 while (i != lines.end ()) {
4898 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4901 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4903 /* check this range against all the AudioRanges that we are using */
4904 list<AudioRange>::const_iterator k = _ranges.begin ();
4905 while (k != _ranges.end()) {
4906 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4912 /* add it to our list if it overlaps at all */
4913 if (k != _ranges.end()) {
4918 _lines.push_back (n);
4924 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4928 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4930 return 1.0 - ((global_y - y_origin) / line->height());
4934 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4936 Drag::start_grab (event, cursor);
4938 /* Get line states before we start changing things */
4939 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4940 i->state = &i->line->get_state ();
4941 i->original_fraction = y_fraction (i->line, current_pointer_y());
4944 if (_ranges.empty()) {
4946 /* No selected time ranges: drag all points */
4947 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4948 uint32_t const N = i->line->npoints ();
4949 for (uint32_t j = 0; j < N; ++j) {
4950 i->points.push_back (i->line->nth (j));
4956 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4958 framecnt_t const half = (i->start + i->end) / 2;
4960 /* find the line that this audio range starts in */
4961 list<Line>::iterator j = _lines.begin();
4962 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4966 if (j != _lines.end()) {
4967 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4969 /* j is the line that this audio range starts in; fade into it;
4970 64 samples length plucked out of thin air.
4973 framepos_t a = i->start + 64;
4978 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4979 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4981 the_list->add (p, the_list->eval (p));
4982 the_list->add (q, the_list->eval (q));
4985 /* same thing for the end */
4988 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4992 if (j != _lines.end()) {
4993 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4995 /* j is the line that this audio range starts in; fade out of it;
4996 64 samples length plucked out of thin air.
4999 framepos_t b = i->end - 64;
5004 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5005 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5007 the_list->add (p, the_list->eval (p));
5008 the_list->add (q, the_list->eval (q));
5012 _nothing_to_drag = true;
5014 /* Find all the points that should be dragged and put them in the relevant
5015 points lists in the Line structs.
5018 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5020 uint32_t const N = i->line->npoints ();
5021 for (uint32_t j = 0; j < N; ++j) {
5023 /* here's a control point on this line */
5024 ControlPoint* p = i->line->nth (j);
5025 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5027 /* see if it's inside a range */
5028 list<AudioRange>::const_iterator k = _ranges.begin ();
5029 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5033 if (k != _ranges.end()) {
5034 /* dragging this point */
5035 _nothing_to_drag = false;
5036 i->points.push_back (p);
5042 if (_nothing_to_drag) {
5046 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5047 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5052 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5054 if (_nothing_to_drag) {
5058 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5059 float const f = y_fraction (l->line, current_pointer_y());
5060 /* we are ignoring x position for this drag, so we can just pass in anything */
5062 l->line->drag_motion (0, f, true, false, ignored);
5063 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5068 AutomationRangeDrag::finished (GdkEvent* event, bool)
5070 if (_nothing_to_drag) {
5074 motion (event, false);
5075 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5076 i->line->end_drag (false, 0);
5079 _editor->session()->commit_reversible_command ();
5083 AutomationRangeDrag::aborted (bool)
5085 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5090 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5092 , initial_time_axis_view (itav)
5094 /* note that time_axis_view may be null if the regionview was created
5095 * as part of a copy operation.
5097 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5098 layer = v->region()->layer ();
5099 initial_y = v->get_canvas_group()->position().y;
5100 initial_playlist = v->region()->playlist ();
5101 initial_position = v->region()->position ();
5102 initial_end = v->region()->position () + v->region()->length ();
5105 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5106 : Drag (e, i->canvas_item ())
5109 , _cumulative_dx (0)
5111 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5112 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5117 PatchChangeDrag::motion (GdkEvent* ev, bool)
5119 framepos_t f = adjusted_current_frame (ev);
5120 boost::shared_ptr<Region> r = _region_view->region ();
5121 f = max (f, r->position ());
5122 f = min (f, r->last_frame ());
5124 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5125 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5126 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5127 _cumulative_dx = dxu;
5131 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5133 if (!movement_occurred) {
5137 boost::shared_ptr<Region> r (_region_view->region ());
5138 framepos_t f = adjusted_current_frame (ev);
5139 f = max (f, r->position ());
5140 f = min (f, r->last_frame ());
5142 _region_view->move_patch_change (
5144 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5149 PatchChangeDrag::aborted (bool)
5151 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5155 PatchChangeDrag::setup_pointer_frame_offset ()
5157 boost::shared_ptr<Region> region = _region_view->region ();
5158 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5161 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5162 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5169 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5171 framepos_t const p = _region_view->region()->position ();
5172 double const y = _region_view->midi_view()->y_position ();
5174 x1 = max ((framepos_t) 0, x1 - p);
5175 x2 = max ((framepos_t) 0, x2 - p);
5176 y1 = max (0.0, y1 - y);
5177 y2 = max (0.0, y2 - y);
5179 _region_view->update_drag_selection (
5180 _editor->sample_to_pixel (x1),
5181 _editor->sample_to_pixel (x2),
5184 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5189 MidiRubberbandSelectDrag::deselect_things ()
5194 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5195 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5198 _vertical_only = true;
5202 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5204 double const y = _region_view->midi_view()->y_position ();
5206 y1 = max (0.0, y1 - y);
5207 y2 = max (0.0, y2 - y);
5209 _region_view->update_vertical_drag_selection (
5212 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5217 MidiVerticalSelectDrag::deselect_things ()
5222 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5223 : RubberbandSelectDrag (e, i)
5229 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5231 if (drag_in_progress) {
5232 /* We just want to select things at the end of the drag, not during it */
5236 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5238 _editor->begin_reversible_command (_("rubberband selection"));
5239 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5240 _editor->commit_reversible_command ();
5244 EditorRubberbandSelectDrag::deselect_things ()
5246 if (!getenv("ARDOUR_SAE")) {
5247 _editor->selection->clear_tracks();
5249 _editor->selection->clear_regions();
5250 _editor->selection->clear_points ();
5251 _editor->selection->clear_lines ();
5254 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5262 NoteCreateDrag::~NoteCreateDrag ()
5268 NoteCreateDrag::grid_frames (framepos_t t) const
5271 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
5276 return _region_view->region_beats_to_region_frames (grid_beats);
5280 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5282 Drag::start_grab (event, cursor);
5284 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5286 framepos_t pf = _drags->current_pointer_frame ();
5287 framecnt_t const g = grid_frames (pf);
5289 /* Hack so that we always snap to the note that we are over, instead of snapping
5290 to the next one if we're more than halfway through the one we're over.
5292 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5296 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5298 MidiStreamView* sv = _region_view->midi_stream_view ();
5299 double const x = _editor->sample_to_pixel (_note[0]);
5300 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5302 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5303 _drag_rect->set_outline_all ();
5304 _drag_rect->set_outline_color (0xffffff99);
5305 _drag_rect->set_fill_color (0xffffff66);
5309 NoteCreateDrag::motion (GdkEvent* event, bool)
5311 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5312 double const x = _editor->sample_to_pixel (_note[1]);
5313 if (_note[1] > _note[0]) {
5314 _drag_rect->set_x1 (x);
5316 _drag_rect->set_x0 (x);
5321 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5323 if (!had_movement) {
5327 framepos_t const start = min (_note[0], _note[1]);
5328 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5330 framecnt_t const g = grid_frames (start);
5331 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5333 if (_editor->snap_mode() == SnapNormal && length < g) {
5334 length = g - one_tick;
5337 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5339 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5343 NoteCreateDrag::y_to_region (double y) const
5346 _region_view->get_canvas_group()->canvas_to_item (x, y);
5351 NoteCreateDrag::aborted (bool)
5356 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5361 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5365 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5367 Drag::start_grab (event, cursor);
5371 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5377 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5380 distance = _drags->current_pointer_x() - grab_x();
5381 len = ar->fade_in()->back()->when;
5383 distance = grab_x() - _drags->current_pointer_x();
5384 len = ar->fade_out()->back()->when;
5387 /* how long should it be ? */
5389 new_length = len + _editor->pixel_to_sample (distance);
5391 /* now check with the region that this is legal */
5393 new_length = ar->verify_xfade_bounds (new_length, start);
5396 arv->reset_fade_in_shape_width (ar, new_length);
5398 arv->reset_fade_out_shape_width (ar, new_length);
5403 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5409 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5412 distance = _drags->current_pointer_x() - grab_x();
5413 len = ar->fade_in()->back()->when;
5415 distance = grab_x() - _drags->current_pointer_x();
5416 len = ar->fade_out()->back()->when;
5419 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5421 _editor->begin_reversible_command ("xfade trim");
5422 ar->playlist()->clear_owned_changes ();
5425 ar->set_fade_in_length (new_length);
5427 ar->set_fade_out_length (new_length);
5430 /* Adjusting the xfade may affect other regions in the playlist, so we need
5431 to get undo Commands from the whole playlist rather than just the
5435 vector<Command*> cmds;
5436 ar->playlist()->rdiff (cmds);
5437 _editor->session()->add_commands (cmds);
5438 _editor->commit_reversible_command ();
5443 CrossfadeEdgeDrag::aborted (bool)
5446 arv->redraw_start_xfade ();
5448 arv->redraw_end_xfade ();
5452 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item)
5453 : Drag (e, item, true)
5454 , line (new EditorCursor (*e))
5456 line->set_position (_editor->get_preferred_edit_position());
5459 RegionCutDrag::~RegionCutDrag ()
5465 RegionCutDrag::motion (GdkEvent*, bool)
5467 line->set_position (_drags->current_pointer_frame());
5471 RegionCutDrag::finished (GdkEvent*, bool)
5474 _editor->split_region ();
5478 RegionCutDrag::aborted (bool)