2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
49 #include "audio_region_view.h"
50 #include "midi_region_view.h"
51 #include "ardour_ui.h"
52 #include "gui_thread.h"
53 #include "control_point.h"
54 #include "region_gain_line.h"
55 #include "editor_drag.h"
56 #include "audio_time_axis.h"
57 #include "midi_time_axis.h"
58 #include "selection.h"
59 #include "midi_selection.h"
60 #include "automation_time_axis.h"
62 #include "editor_cursors.h"
63 #include "mouse_cursors.h"
64 #include "note_base.h"
65 #include "patch_change.h"
66 #include "verbose_cursor.h"
69 using namespace ARDOUR;
72 using namespace Gtkmm2ext;
73 using namespace Editing;
74 using namespace ArdourCanvas;
76 using Gtkmm2ext::Keyboard;
78 double ControlPointDrag::_zero_gain_fraction = -1.0;
80 DragManager::DragManager (Editor* e)
83 , _current_pointer_frame (0)
87 DragManager::~DragManager ()
92 /** Call abort for each active drag */
98 cerr << "Aborting drag\n";
100 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
105 if (!_drags.empty ()) {
106 _editor->set_follow_playhead (_old_follow_playhead, false);
115 DragManager::add (Drag* d)
117 d->set_manager (this);
118 _drags.push_back (d);
122 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
124 d->set_manager (this);
125 _drags.push_back (d);
130 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
132 /* Prevent follow playhead during the drag to be nice to the user */
133 _old_follow_playhead = _editor->follow_playhead ();
134 _editor->set_follow_playhead (false);
136 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
138 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
139 (*i)->start_grab (e, c);
143 /** Call end_grab for each active drag.
144 * @return true if any drag reported movement having occurred.
147 DragManager::end_grab (GdkEvent* e)
152 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
153 bool const t = (*i)->end_grab (e);
164 _editor->set_follow_playhead (_old_follow_playhead, false);
170 DragManager::mark_double_click ()
172 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
173 (*i)->set_double_click (true);
178 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
182 /* calling this implies that we expect the event to have canvas
185 * Can we guarantee that this is true?
188 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
190 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
191 bool const t = (*i)->motion_handler (e, from_autoscroll);
192 /* run all handlers; return true if at least one of them
193 returns true (indicating that the event has been handled).
205 DragManager::have_item (ArdourCanvas::Item* i) const
207 list<Drag*>::const_iterator j = _drags.begin ();
208 while (j != _drags.end() && (*j)->item () != i) {
212 return j != _drags.end ();
215 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
218 , _pointer_frame_offset (0)
219 , _trackview_only (trackview_only)
220 , _move_threshold_passed (false)
221 , _was_double_click (false)
222 , _raw_grab_frame (0)
224 , _last_pointer_frame (0)
230 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
243 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
245 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
247 if (Keyboard::is_button2_event (&event->button)) {
248 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
249 _y_constrained = true;
250 _x_constrained = false;
252 _y_constrained = false;
253 _x_constrained = true;
256 _x_constrained = false;
257 _y_constrained = false;
260 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
261 setup_pointer_frame_offset ();
262 _grab_frame = adjusted_frame (_raw_grab_frame, event);
263 _last_pointer_frame = _grab_frame;
264 _last_pointer_x = _grab_x;
266 if (_trackview_only) {
267 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
270 _last_pointer_y = _grab_y;
275 /* CAIROCANVAS need a variant here that passes *cursor */
277 _editor->push_canvas_cursor (cursor);
280 if (_editor->session() && _editor->session()->transport_rolling()) {
283 _was_rolling = false;
286 switch (_editor->snap_type()) {
287 case SnapToRegionStart:
288 case SnapToRegionEnd:
289 case SnapToRegionSync:
290 case SnapToRegionBoundary:
291 _editor->build_region_boundary_cache ();
298 /** Call to end a drag `successfully'. Ungrabs item and calls
299 * subclass' finished() method.
301 * @param event GDK event, or 0.
302 * @return true if some movement occurred, otherwise false.
305 Drag::end_grab (GdkEvent* event)
307 _editor->stop_canvas_autoscroll ();
311 finished (event, _move_threshold_passed);
313 _editor->verbose_cursor()->hide ();
314 _editor->pop_canvas_cursor ();
316 return _move_threshold_passed;
320 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
324 if (f > _pointer_frame_offset) {
325 pos = f - _pointer_frame_offset;
329 _editor->snap_to_with_modifier (pos, event);
336 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
338 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
342 Drag::current_pointer_y () const
344 if (!_trackview_only) {
345 return _drags->current_pointer_y ();
348 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
352 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
354 /* check to see if we have moved in any way that matters since the last motion event */
355 if (_move_threshold_passed &&
356 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
357 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
361 pair<framecnt_t, int> const threshold = move_threshold ();
363 bool const old_move_threshold_passed = _move_threshold_passed;
365 if (!from_autoscroll && !_move_threshold_passed) {
367 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
368 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
370 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
373 if (active (_editor->mouse_mode) && _move_threshold_passed) {
375 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
376 if (!from_autoscroll) {
377 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
380 if (!_editor->autoscroll_active() || from_autoscroll) {
381 motion (event, _move_threshold_passed != old_move_threshold_passed);
383 _last_pointer_x = _drags->current_pointer_x ();
384 _last_pointer_y = current_pointer_y ();
385 _last_pointer_frame = adjusted_current_frame (event);
395 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
403 aborted (_move_threshold_passed);
405 _editor->stop_canvas_autoscroll ();
406 _editor->verbose_cursor()->hide ();
410 Drag::show_verbose_cursor_time (framepos_t frame)
412 _editor->verbose_cursor()->set_time (frame);
413 _editor->verbose_cursor()->show ();
417 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
419 _editor->verbose_cursor()->set_duration (start, end);
420 _editor->verbose_cursor()->show ();
424 Drag::show_verbose_cursor_text (string const & text)
426 _editor->verbose_cursor()->set (text);
427 _editor->verbose_cursor()->show ();
430 boost::shared_ptr<Region>
431 Drag::add_midi_region (MidiTimeAxisView* view)
433 if (_editor->session()) {
434 const TempoMap& map (_editor->session()->tempo_map());
435 framecnt_t pos = grab_frame();
436 const Meter& m = map.meter_at (pos);
437 /* not that the frame rate used here can be affected by pull up/down which
440 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
441 return view->add_region (grab_frame(), len, true);
444 return boost::shared_ptr<Region>();
447 struct EditorOrderTimeAxisViewSorter {
448 bool operator() (TimeAxisView* a, TimeAxisView* b) {
449 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
450 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
452 return ra->route()->order_key () < rb->route()->order_key ();
456 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
460 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
462 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
463 as some of the regions we are dragging may be on such tracks.
466 TrackViewList track_views = _editor->track_views;
467 track_views.sort (EditorOrderTimeAxisViewSorter ());
469 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
470 _time_axis_views.push_back (*i);
472 TimeAxisView::Children children_list = (*i)->get_child_list ();
473 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
474 _time_axis_views.push_back (j->get());
478 /* the list of views can be empty at this point if this is a region list-insert drag
481 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
482 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
485 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
489 RegionDrag::region_going_away (RegionView* v)
491 list<DraggingView>::iterator i = _views.begin ();
492 while (i != _views.end() && i->view != v) {
496 if (i != _views.end()) {
501 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
502 * or -1 if it is not found.
505 RegionDrag::find_time_axis_view (TimeAxisView* t) const
508 int const N = _time_axis_views.size ();
509 while (i < N && _time_axis_views[i] != t) {
520 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
521 : RegionDrag (e, i, p, v)
524 , _last_pointer_time_axis_view (0)
525 , _last_pointer_layer (0)
527 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
531 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
533 Drag::start_grab (event, cursor);
535 show_verbose_cursor_time (_last_frame_position);
537 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
539 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
540 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
545 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
547 /* compute the amount of pointer motion in frames, and where
548 the region would be if we moved it by that much.
550 *pending_region_position = adjusted_current_frame (event);
552 framepos_t sync_frame;
553 framecnt_t sync_offset;
556 sync_offset = _primary->region()->sync_offset (sync_dir);
558 /* we don't handle a sync point that lies before zero.
560 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
562 sync_frame = *pending_region_position + (sync_dir*sync_offset);
564 _editor->snap_to_with_modifier (sync_frame, event);
566 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
569 *pending_region_position = _last_frame_position;
572 if (*pending_region_position > max_framepos - _primary->region()->length()) {
573 *pending_region_position = _last_frame_position;
578 /* in locked edit mode, reverse the usual meaning of _x_constrained */
579 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
581 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
583 /* x movement since last time (in pixels) */
584 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
586 /* total x movement */
587 framecnt_t total_dx = *pending_region_position;
588 if (regions_came_from_canvas()) {
589 total_dx = total_dx - grab_frame ();
592 /* check that no regions have gone off the start of the session */
593 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
594 if ((i->view->region()->position() + total_dx) < 0) {
596 *pending_region_position = _last_frame_position;
607 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
609 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
610 int const n = i->time_axis_view + delta_track;
611 if (n < 0 || n >= int (_time_axis_views.size())) {
612 /* off the top or bottom track */
616 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
617 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
618 /* not a track, or the wrong type */
622 double const l = i->layer + delta_layer;
624 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
625 mode to allow the user to place a region below another on layer 0.
627 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
628 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
629 If it has, the layers will be munged later anyway, so it's ok.
635 /* all regions being dragged are ok with this change */
640 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
642 double delta_layer = 0;
643 int delta_time_axis_view = 0;
645 assert (!_views.empty ());
647 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
649 /* Find the TimeAxisView that the pointer is now over */
650 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
651 TimeAxisView* tv = r.first;
653 if (tv && tv->view()) {
654 double layer = r.second;
656 if (first_move && tv->view()->layer_display() == Stacked) {
657 tv->view()->set_layer_display (Expanded);
660 /* Here's the current pointer position in terms of time axis view and layer */
661 int const current_pointer_time_axis_view = find_time_axis_view (tv);
662 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
664 /* Work out the change in y */
666 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
667 delta_layer = current_pointer_layer - _last_pointer_layer;
670 /* Work out the change in x */
671 framepos_t pending_region_position;
672 double const x_delta = compute_x_delta (event, &pending_region_position);
673 _last_frame_position = pending_region_position;
675 /* Verify change in y */
676 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
677 /* this y movement is not allowed, so do no y movement this time */
678 delta_time_axis_view = 0;
682 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
683 /* haven't reached next snap point, and we're not switching
684 trackviews nor layers. nothing to do.
689 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
691 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
693 RegionView* rv = i->view;
695 if (rv->region()->locked() || rv->region()->video_locked()) {
702 /* reparent the regionview into a group above all
706 ArdourCanvas::Item* rvg = rv->get_canvas_group();
707 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
708 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
709 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
710 /* move the item so that it continues to appear at the
711 same location now that its parent has changed.
713 rvg->move (rv_canvas_offset - dmg_canvas_offset);
716 /* If we have moved tracks, we'll fudge the layer delta so that the
717 region gets moved back onto layer 0 on its new track; this avoids
718 confusion when dragging regions from non-zero layers onto different
721 double this_delta_layer = delta_layer;
722 if (delta_time_axis_view != 0) {
723 this_delta_layer = - i->layer;
730 if (i->time_axis_view >= 0) {
731 track_index = i->time_axis_view + delta_time_axis_view;
733 track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
736 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
740 /* The TimeAxisView that this region is now over */
741 TimeAxisView* current_tv = _time_axis_views[track_index];
743 /* Ensure it is moved from stacked -> expanded if appropriate */
744 if (current_tv->view()->layer_display() == Stacked) {
745 current_tv->view()->set_layer_display (Expanded);
748 /* We're only allowed to go -ve in layer on Expanded views */
749 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
750 this_delta_layer = - i->layer;
754 rv->set_height (current_tv->view()->child_height ());
756 /* Update show/hidden status as the region view may have come from a hidden track,
757 or have moved to one.
759 if (current_tv->hidden ()) {
760 rv->get_canvas_group()->hide ();
762 rv->get_canvas_group()->show ();
765 /* Update the DraggingView */
766 i->time_axis_view = track_index;
767 i->layer += this_delta_layer;
770 _editor->mouse_brush_insert_region (rv, pending_region_position);
774 /* Get the y coordinate of the top of the track that this region is now over */
775 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
777 /* And adjust for the layer that it should be on */
778 StreamView* cv = current_tv->view ();
779 switch (cv->layer_display ()) {
783 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
786 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
790 /* need to get the parent of the regionview
791 * canvas group and get its position in
792 * equivalent coordinate space as the trackview
793 * we are now dragging over.
796 /* Now move the region view */
797 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->canvas_origin().y);
801 /* Only move the region into the empty dropzone at the bottom if the pointer
805 if (current_pointer_y() >= 0) {
807 Coord last_track_bottom_edge;
808 if (!_time_axis_views.empty()) {
809 TimeAxisView* last = _time_axis_views.back();
810 last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
812 last_track_bottom_edge = 0;
815 rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y);
816 i->time_axis_view = -1;
820 } /* foreach region */
822 _total_x_delta += x_delta;
824 if (x_delta != 0 && !_brushing) {
825 show_verbose_cursor_time (_last_frame_position);
828 _last_pointer_time_axis_view += delta_time_axis_view;
829 _last_pointer_layer += delta_layer;
833 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
835 if (_copy && first_move) {
837 /* duplicate the regionview(s) and region(s) */
839 list<DraggingView> new_regionviews;
841 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
843 RegionView* rv = i->view;
844 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
845 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
847 const boost::shared_ptr<const Region> original = rv->region();
848 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
849 region_copy->set_position (original->position());
853 boost::shared_ptr<AudioRegion> audioregion_copy
854 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
856 nrv = new AudioRegionView (*arv, audioregion_copy);
858 boost::shared_ptr<MidiRegion> midiregion_copy
859 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
860 nrv = new MidiRegionView (*mrv, midiregion_copy);
865 nrv->get_canvas_group()->show ();
866 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
868 /* swap _primary to the copy */
870 if (rv == _primary) {
874 /* ..and deselect the one we copied */
876 rv->set_selected (false);
879 if (!new_regionviews.empty()) {
881 /* reflect the fact that we are dragging the copies */
883 _views = new_regionviews;
885 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
889 RegionMotionDrag::motion (event, first_move);
893 RegionMotionDrag::finished (GdkEvent *, bool)
895 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
900 if ((*i)->view()->layer_display() == Expanded) {
901 (*i)->view()->set_layer_display (Stacked);
907 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
909 RegionMotionDrag::finished (ev, movement_occurred);
911 if (!movement_occurred) {
915 if (was_double_click() && !_views.empty()) {
916 DraggingView dv = _views.front();
917 dv.view->show_region_editor ();
924 /* reverse this here so that we have the correct logic to finalize
928 if (Config->get_edit_mode() == Lock) {
929 _x_constrained = !_x_constrained;
932 assert (!_views.empty ());
934 /* We might have hidden region views so that they weren't visible during the drag
935 (when they have been reparented). Now everything can be shown again, as region
936 views are back in their track parent groups.
938 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
939 i->view->get_canvas_group()->show ();
942 bool const changed_position = (_last_frame_position != _primary->region()->position());
943 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
944 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
964 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
968 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
970 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
975 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
976 list<boost::shared_ptr<AudioTrack> > audio_tracks;
977 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
978 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
980 rtav->set_height (original->current_height());
984 ChanCount one_midi_port (DataType::MIDI, 1);
985 list<boost::shared_ptr<MidiTrack> > midi_tracks;
986 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
987 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
989 rtav->set_height (original->current_height());
994 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1000 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1002 RegionSelection new_views;
1003 PlaylistSet modified_playlists;
1004 RouteTimeAxisView* new_time_axis_view = 0;
1007 /* all changes were made during motion event handlers */
1009 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1013 _editor->commit_reversible_command ();
1017 if (_x_constrained) {
1018 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1020 _editor->begin_reversible_command (Operations::region_copy);
1023 /* insert the regions into their new playlists */
1024 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1026 RouteTimeAxisView* dest_rtv = 0;
1028 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1034 if (changed_position && !_x_constrained) {
1035 where = i->view->region()->position() - drag_delta;
1037 where = i->view->region()->position();
1040 if (i->time_axis_view < 0) {
1041 if (!new_time_axis_view) {
1042 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1044 dest_rtv = new_time_axis_view;
1046 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1049 if (dest_rtv != 0) {
1050 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1051 if (new_view != 0) {
1052 new_views.push_back (new_view);
1056 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1057 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1060 list<DraggingView>::const_iterator next = i;
1066 /* If we've created new regions either by copying or moving
1067 to a new track, we want to replace the old selection with the new ones
1070 if (new_views.size() > 0) {
1071 _editor->selection->set (new_views);
1074 /* write commands for the accumulated diffs for all our modified playlists */
1075 add_stateful_diff_commands_for_playlists (modified_playlists);
1077 _editor->commit_reversible_command ();
1081 RegionMoveDrag::finished_no_copy (
1082 bool const changed_position,
1083 bool const changed_tracks,
1084 framecnt_t const drag_delta
1087 RegionSelection new_views;
1088 PlaylistSet modified_playlists;
1089 PlaylistSet frozen_playlists;
1090 set<RouteTimeAxisView*> views_to_update;
1091 RouteTimeAxisView* new_time_axis_view = 0;
1094 /* all changes were made during motion event handlers */
1095 _editor->commit_reversible_command ();
1099 if (_x_constrained) {
1100 _editor->begin_reversible_command (_("fixed time region drag"));
1102 _editor->begin_reversible_command (Operations::region_drag);
1105 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1107 RegionView* rv = i->view;
1108 RouteTimeAxisView* dest_rtv = 0;
1110 if (rv->region()->locked() || rv->region()->video_locked()) {
1115 if (i->time_axis_view < 0) {
1116 if (!new_time_axis_view) {
1117 new_time_axis_view = create_destination_time_axis (rv->region(), i->initial_time_axis_view);
1119 dest_rtv = new_time_axis_view;
1121 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1126 double const dest_layer = i->layer;
1128 views_to_update.insert (dest_rtv);
1132 if (changed_position && !_x_constrained) {
1133 where = rv->region()->position() - drag_delta;
1135 where = rv->region()->position();
1138 if (changed_tracks) {
1140 /* insert into new playlist */
1142 RegionView* new_view = insert_region_into_playlist (
1143 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1146 if (new_view == 0) {
1151 new_views.push_back (new_view);
1153 /* remove from old playlist */
1155 /* the region that used to be in the old playlist is not
1156 moved to the new one - we use a copy of it. as a result,
1157 any existing editor for the region should no longer be
1160 rv->hide_region_editor();
1163 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1167 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1169 /* this movement may result in a crossfade being modified, or a layering change,
1170 so we need to get undo data from the playlist as well as the region.
1173 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1175 playlist->clear_changes ();
1178 rv->region()->clear_changes ();
1181 motion on the same track. plonk the previously reparented region
1182 back to its original canvas group (its streamview).
1183 No need to do anything for copies as they are fake regions which will be deleted.
1186 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1187 rv->get_canvas_group()->set_y_position (i->initial_y);
1190 /* just change the model */
1191 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1192 playlist->set_layer (rv->region(), dest_layer);
1195 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1197 r = frozen_playlists.insert (playlist);
1200 playlist->freeze ();
1203 rv->region()->set_position (where);
1205 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1208 if (changed_tracks) {
1210 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1211 was selected in all of them, then removing it from a playlist will have removed all
1212 trace of it from _views (i.e. there were N regions selected, we removed 1,
1213 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1214 corresponding regionview, and _views is now empty).
1216 This could have invalidated any and all iterators into _views.
1218 The heuristic we use here is: if the region selection is empty, break out of the loop
1219 here. if the region selection is not empty, then restart the loop because we know that
1220 we must have removed at least the region(view) we've just been working on as well as any
1221 that we processed on previous iterations.
1223 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1224 we can just iterate.
1228 if (_views.empty()) {
1239 /* If we've created new regions either by copying or moving
1240 to a new track, we want to replace the old selection with the new ones
1243 if (new_views.size() > 0) {
1244 _editor->selection->set (new_views);
1247 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1251 /* write commands for the accumulated diffs for all our modified playlists */
1252 add_stateful_diff_commands_for_playlists (modified_playlists);
1254 _editor->commit_reversible_command ();
1256 /* We have futzed with the layering of canvas items on our streamviews.
1257 If any region changed layer, this will have resulted in the stream
1258 views being asked to set up their region views, and all will be well.
1259 If not, we might now have badly-ordered region views. Ask the StreamViews
1260 involved to sort themselves out, just in case.
1263 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1264 (*i)->view()->playlist_layered ((*i)->track ());
1268 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1269 * @param region Region to remove.
1270 * @param playlist playlist To remove from.
1271 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1272 * that clear_changes () is only called once per playlist.
1275 RegionMoveDrag::remove_region_from_playlist (
1276 boost::shared_ptr<Region> region,
1277 boost::shared_ptr<Playlist> playlist,
1278 PlaylistSet& modified_playlists
1281 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1284 playlist->clear_changes ();
1287 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1291 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1292 * clearing the playlist's diff history first if necessary.
1293 * @param region Region to insert.
1294 * @param dest_rtv Destination RouteTimeAxisView.
1295 * @param dest_layer Destination layer.
1296 * @param where Destination position.
1297 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1298 * that clear_changes () is only called once per playlist.
1299 * @return New RegionView, or 0 if no insert was performed.
1302 RegionMoveDrag::insert_region_into_playlist (
1303 boost::shared_ptr<Region> region,
1304 RouteTimeAxisView* dest_rtv,
1307 PlaylistSet& modified_playlists
1310 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1311 if (!dest_playlist) {
1315 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1316 _new_region_view = 0;
1317 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1319 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1320 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1322 dest_playlist->clear_changes ();
1325 dest_playlist->add_region (region, where);
1327 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1328 dest_playlist->set_layer (region, dest_layer);
1333 assert (_new_region_view);
1335 return _new_region_view;
1339 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1341 _new_region_view = rv;
1345 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1347 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1348 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1350 _editor->session()->add_command (c);
1359 RegionMoveDrag::aborted (bool movement_occurred)
1363 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1370 RegionMotionDrag::aborted (movement_occurred);
1375 RegionMotionDrag::aborted (bool)
1377 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1379 StreamView* sview = (*i)->view();
1382 if (sview->layer_display() == Expanded) {
1383 sview->set_layer_display (Stacked);
1388 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1389 RegionView* rv = i->view;
1390 TimeAxisView* tv = &(rv->get_time_axis_view ());
1391 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1393 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1394 rv->get_canvas_group()->set_y_position (0);
1396 rv->move (-_total_x_delta, 0);
1397 rv->set_height (rtv->view()->child_height ());
1401 /** @param b true to brush, otherwise false.
1402 * @param c true to make copies of the regions being moved, otherwise false.
1404 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1405 : RegionMotionDrag (e, i, p, v, b),
1408 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1411 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1412 if (rtv && rtv->is_track()) {
1413 speed = rtv->track()->speed ();
1416 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1420 RegionMoveDrag::setup_pointer_frame_offset ()
1422 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1425 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1426 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1428 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1430 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1431 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1433 _primary = v->view()->create_region_view (r, false, false);
1435 _primary->get_canvas_group()->show ();
1436 _primary->set_position (pos, 0);
1437 _views.push_back (DraggingView (_primary, this, v));
1439 _last_frame_position = pos;
1441 _item = _primary->get_canvas_group ();
1445 RegionInsertDrag::finished (GdkEvent *, bool)
1447 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1449 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1450 _primary->get_canvas_group()->set_y_position (0);
1452 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1454 _editor->begin_reversible_command (Operations::insert_region);
1455 playlist->clear_changes ();
1456 playlist->add_region (_primary->region (), _last_frame_position);
1458 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1459 if (Config->get_edit_mode() == Ripple) {
1460 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1463 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1464 _editor->commit_reversible_command ();
1472 RegionInsertDrag::aborted (bool)
1479 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1480 : RegionMoveDrag (e, i, p, v, false, false)
1482 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1485 struct RegionSelectionByPosition {
1486 bool operator() (RegionView*a, RegionView* b) {
1487 return a->region()->position () < b->region()->position();
1492 RegionSpliceDrag::motion (GdkEvent* event, bool)
1494 /* Which trackview is this ? */
1496 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1497 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1499 /* The region motion is only processed if the pointer is over
1503 if (!tv || !tv->is_track()) {
1504 /* To make sure we hide the verbose canvas cursor when the mouse is
1505 not held over an audio track.
1507 _editor->verbose_cursor()->hide ();
1510 _editor->verbose_cursor()->show ();
1515 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1521 RegionSelection copy;
1522 _editor->selection->regions.by_position(copy);
1524 framepos_t const pf = adjusted_current_frame (event);
1526 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1528 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1534 boost::shared_ptr<Playlist> playlist;
1536 if ((playlist = atv->playlist()) == 0) {
1540 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1545 if (pf < (*i)->region()->last_frame() + 1) {
1549 if (pf > (*i)->region()->first_frame()) {
1555 playlist->shuffle ((*i)->region(), dir);
1560 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1562 RegionMoveDrag::finished (event, movement_occurred);
1566 RegionSpliceDrag::aborted (bool)
1576 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1579 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1581 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1582 RegionSelection to_ripple;
1583 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1584 if ((*i)->position() >= where) {
1585 to_ripple.push_back (rtv->view()->find_view(*i));
1589 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1590 if (!exclude.contains (*i)) {
1591 // the selection has already been added to _views
1593 if (drag_in_progress) {
1594 // do the same things that RegionMotionDrag::motion does when
1595 // first_move is true, for the region views that we're adding
1596 // to _views this time
1599 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1600 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1601 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1602 rvg->reparent (_editor->_drag_motion_group);
1604 // we only need to move in the y direction
1605 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1610 _views.push_back (DraggingView (*i, this, tav));
1616 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1619 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1620 // we added all the regions after the selection
1622 std::list<DraggingView>::iterator to_erase = i++;
1623 if (!_editor->selection->regions.contains (to_erase->view)) {
1624 // restore the non-selected regions to their original playlist & positions,
1625 // and then ripple them back by the length of the regions that were dragged away
1626 // do the same things as RegionMotionDrag::aborted
1628 RegionView *rv = to_erase->view;
1629 TimeAxisView* tv = &(rv->get_time_axis_view ());
1630 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1633 // plonk them back onto their own track
1634 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1635 rv->get_canvas_group()->set_y_position (0);
1639 // move the underlying region to match the view
1640 rv->region()->set_position (rv->region()->position() + amount);
1642 // restore the view to match the underlying region's original position
1643 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
1646 rv->set_height (rtv->view()->child_height ());
1647 _views.erase (to_erase);
1653 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const
1655 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) {
1657 return allow_moves_across_tracks;
1665 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1666 : RegionMoveDrag (e, i, p, v, false, false)
1668 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
1669 // compute length of selection
1670 RegionSelection selected_regions = _editor->selection->regions;
1671 selection_length = selected_regions.end_frame() - selected_regions.start();
1673 // we'll only allow dragging to another track in ripple mode if all the regions
1674 // being dragged start off on the same track
1675 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
1678 exclude = new RegionList;
1679 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
1680 exclude->push_back((*i)->region());
1683 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
1684 RegionSelection copy;
1685 selected_regions.by_position(copy); // get selected regions sorted by position into copy
1687 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
1688 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1690 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1691 // find ripple start point on each applicable playlist
1692 RegionView *first_selected_on_this_track = NULL;
1693 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1694 if ((*i)->region()->playlist() == (*pi)) {
1695 // region is on this playlist - it's the first, because they're sorted
1696 first_selected_on_this_track = *i;
1700 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
1701 add_all_after_to_views (
1702 &first_selected_on_this_track->get_time_axis_view(),
1703 first_selected_on_this_track->region()->position(),
1704 selected_regions, false);
1707 if (allow_moves_across_tracks) {
1708 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
1716 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
1718 /* Which trackview is this ? */
1720 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1721 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1723 /* The region motion is only processed if the pointer is over
1727 if (!tv || !tv->is_track()) {
1728 /* To make sure we hide the verbose canvas cursor when the mouse is
1729 not held over an audiotrack.
1731 _editor->verbose_cursor()->hide ();
1735 framepos_t where = adjusted_current_frame (event);
1736 assert (where >= 0);
1738 double delta = compute_x_delta (event, &after);
1740 framecnt_t amount = _editor->pixel_to_sample (delta);
1742 if (allow_moves_across_tracks) {
1743 // all the originally selected regions were on the same track
1745 framecnt_t adjust = 0;
1746 if (prev_tav && tv != prev_tav) {
1747 // dragged onto a different track
1748 // remove the unselected regions from _views, restore them to their original positions
1749 // and add the regions after the drop point on the new playlist to _views instead.
1750 // undo the effect of rippling the previous playlist, and include the effect of removing
1751 // the dragged region(s) from this track
1753 remove_unselected_from_views (prev_amount, false);
1754 // ripple previous playlist according to the regions that have been removed onto the new playlist
1755 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
1758 // move just the selected regions
1759 RegionMoveDrag::motion(event, first_move);
1761 // ensure that the ripple operation on the new playlist inserts selection_length time
1762 adjust = selection_length;
1763 // ripple the new current playlist
1764 tv->playlist()->ripple (where, amount+adjust, exclude);
1766 // add regions after point where drag entered this track to subsequent ripples
1767 add_all_after_to_views (tv, where, _editor->selection->regions, true);
1770 // motion on same track
1771 RegionMoveDrag::motion(event, first_move);
1775 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
1776 prev_position = where;
1778 // selection encompasses multiple tracks - just drag
1779 // cross-track drags are forbidden
1780 RegionMoveDrag::motion(event, first_move);
1783 if (!_x_constrained) {
1784 prev_amount += amount;
1787 _last_frame_position = after;
1791 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
1793 if (!movement_occurred) {
1797 if (was_double_click() && !_views.empty()) {
1798 DraggingView dv = _views.front();
1799 dv.view->show_region_editor ();
1806 _editor->begin_reversible_command(_("Ripple drag"));
1808 // remove the regions being rippled from the dragging view, updating them to
1809 // their new positions
1810 remove_unselected_from_views (prev_amount, true);
1812 if (allow_moves_across_tracks) {
1814 // if regions were dragged across tracks, we've rippled any later
1815 // regions on the track the regions were dragged off, so we need
1816 // to add the original track to the undo record
1817 orig_tav->playlist()->clear_changes();
1818 vector<Command*> cmds;
1819 orig_tav->playlist()->rdiff (cmds);
1820 _editor->session()->add_commands (cmds);
1822 if (prev_tav && prev_tav != orig_tav) {
1823 prev_tav->playlist()->clear_changes();
1824 vector<Command*> cmds;
1825 prev_tav->playlist()->rdiff (cmds);
1826 _editor->session()->add_commands (cmds);
1829 // selection spanned multiple tracks - all will need adding to undo record
1831 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
1832 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1834 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1835 (*pi)->clear_changes();
1836 vector<Command*> cmds;
1837 (*pi)->rdiff (cmds);
1838 _editor->session()->add_commands (cmds);
1842 // other modified playlists are added to undo by RegionMoveDrag::finished()
1843 RegionMoveDrag::finished (event, movement_occurred);
1844 _editor->commit_reversible_command();
1848 RegionRippleDrag::aborted (bool movement_occurred)
1850 RegionMoveDrag::aborted (movement_occurred);
1855 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1857 _view (dynamic_cast<MidiTimeAxisView*> (v))
1859 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1865 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1868 _region = add_midi_region (_view);
1869 _view->playlist()->freeze ();
1872 framepos_t const f = adjusted_current_frame (event);
1873 if (f < grab_frame()) {
1874 _region->set_position (f);
1877 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1878 so that if this region is duplicated, its duplicate starts on
1879 a snap point rather than 1 frame after a snap point. Otherwise things get
1880 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1881 place snapped notes at the start of the region.
1884 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1885 _region->set_length (len < 1 ? 1 : len);
1891 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1893 if (!movement_occurred) {
1894 add_midi_region (_view);
1896 _view->playlist()->thaw ();
1901 RegionCreateDrag::aborted (bool)
1904 _view->playlist()->thaw ();
1910 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1914 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1918 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1920 Gdk::Cursor* cursor;
1921 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1923 float x_fraction = cnote->mouse_x_fraction ();
1925 if (x_fraction > 0.0 && x_fraction < 0.25) {
1926 cursor = _editor->cursors()->left_side_trim;
1928 cursor = _editor->cursors()->right_side_trim;
1931 Drag::start_grab (event, cursor);
1933 region = &cnote->region_view();
1935 double const region_start = region->get_position_pixels();
1936 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1938 if (grab_x() <= middle_point) {
1939 cursor = _editor->cursors()->left_side_trim;
1942 cursor = _editor->cursors()->right_side_trim;
1948 if (event->motion.state & Keyboard::PrimaryModifier) {
1954 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1956 if (ms.size() > 1) {
1957 /* has to be relative, may make no sense otherwise */
1961 /* select this note; if it is already selected, preserve the existing selection,
1962 otherwise make this note the only one selected.
1964 region->note_selected (cnote, cnote->selected ());
1966 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1967 MidiRegionSelection::iterator next;
1970 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
1972 mrv->begin_resizing (at_front);
1979 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1981 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1982 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1983 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1985 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
1987 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1993 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1995 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1996 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1997 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1999 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2001 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2007 NoteResizeDrag::aborted (bool)
2009 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2010 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2011 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2013 mrv->abort_resizing ();
2018 AVDraggingView::AVDraggingView (RegionView* v)
2021 initial_position = v->region()->position ();
2024 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2027 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2030 TrackViewList empty;
2032 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2033 std::list<RegionView*> views = rs.by_layer();
2035 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2036 RegionView* rv = (*i);
2037 if (!rv->region()->video_locked()) {
2040 _views.push_back (AVDraggingView (rv));
2045 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2047 Drag::start_grab (event);
2048 if (_editor->session() == 0) {
2052 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2053 _max_backwards_drag = (
2054 ARDOUR_UI::instance()->video_timeline->get_duration()
2055 + ARDOUR_UI::instance()->video_timeline->get_offset()
2056 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2059 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2060 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2061 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2064 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2067 Timecode::Time timecode;
2068 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2069 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);
2070 show_verbose_cursor_text (buf);
2074 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2076 if (_editor->session() == 0) {
2079 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2083 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2084 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2086 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2087 dt = - _max_backwards_drag;
2090 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2091 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2093 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2094 RegionView* rv = i->view;
2095 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2098 rv->region()->clear_changes ();
2099 rv->region()->suspend_property_changes();
2101 rv->region()->set_position(i->initial_position + dt);
2102 rv->region_changed(ARDOUR::Properties::position);
2105 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2106 Timecode::Time timecode;
2107 Timecode::Time timediff;
2109 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2110 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2111 snprintf (buf, sizeof (buf),
2112 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2113 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2114 , _("Video Start:"),
2115 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2117 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2119 show_verbose_cursor_text (buf);
2123 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2125 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2129 if (!movement_occurred || ! _editor->session()) {
2133 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2135 _editor->begin_reversible_command (_("Move Video"));
2137 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2138 ARDOUR_UI::instance()->video_timeline->save_undo();
2139 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2140 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2142 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2143 i->view->drag_end();
2144 i->view->region()->resume_property_changes ();
2146 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2149 _editor->session()->maybe_update_session_range(
2150 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2151 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2155 _editor->commit_reversible_command ();
2159 VideoTimeLineDrag::aborted (bool)
2161 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2164 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2165 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2167 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2168 i->view->region()->resume_property_changes ();
2169 i->view->region()->set_position(i->initial_position);
2173 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2174 : RegionDrag (e, i, p, v)
2175 , _preserve_fade_anchor (preserve_fade_anchor)
2176 , _jump_position_when_done (false)
2178 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2182 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2185 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2186 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2188 if (tv && tv->is_track()) {
2189 speed = tv->track()->speed();
2192 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2193 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2194 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2196 framepos_t const pf = adjusted_current_frame (event);
2198 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2199 /* Move the contents of the region around without changing the region bounds */
2200 _operation = ContentsTrim;
2201 Drag::start_grab (event, _editor->cursors()->trimmer);
2203 /* These will get overridden for a point trim.*/
2204 if (pf < (region_start + region_length/2)) {
2205 /* closer to front */
2206 _operation = StartTrim;
2208 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2209 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2211 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2215 _operation = EndTrim;
2216 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2217 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2219 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2224 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2225 _jump_position_when_done = true;
2228 switch (_operation) {
2230 show_verbose_cursor_time (region_start);
2231 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2232 i->view->trim_front_starting ();
2236 show_verbose_cursor_time (region_end);
2239 show_verbose_cursor_time (pf);
2243 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2244 i->view->region()->suspend_property_changes ();
2249 TrimDrag::motion (GdkEvent* event, bool first_move)
2251 RegionView* rv = _primary;
2254 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2255 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2256 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2257 frameoffset_t frame_delta = 0;
2259 if (tv && tv->is_track()) {
2260 speed = tv->track()->speed();
2263 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2269 switch (_operation) {
2271 trim_type = "Region start trim";
2274 trim_type = "Region end trim";
2277 trim_type = "Region content trim";
2284 _editor->begin_reversible_command (trim_type);
2286 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2287 RegionView* rv = i->view;
2288 rv->enable_display (false);
2289 rv->region()->playlist()->clear_owned_changes ();
2291 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2294 arv->temporarily_hide_envelope ();
2298 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2299 insert_result = _editor->motion_frozen_playlists.insert (pl);
2301 if (insert_result.second) {
2307 bool non_overlap_trim = false;
2309 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2310 non_overlap_trim = true;
2313 /* contstrain trim to fade length */
2314 if (_preserve_fade_anchor) {
2315 switch (_operation) {
2317 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2318 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2320 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2321 if (ar->locked()) continue;
2322 framecnt_t len = ar->fade_in()->back()->when;
2323 if (len < dt) dt = min(dt, len);
2327 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2328 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2330 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2331 if (ar->locked()) continue;
2332 framecnt_t len = ar->fade_out()->back()->when;
2333 if (len < -dt) dt = max(dt, -len);
2342 switch (_operation) {
2344 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2345 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2346 if (changed && _preserve_fade_anchor) {
2347 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2349 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2350 framecnt_t len = ar->fade_in()->back()->when;
2351 framecnt_t diff = ar->first_frame() - i->initial_position;
2352 framepos_t new_length = len - diff;
2353 i->anchored_fade_length = min (ar->length(), new_length);
2354 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2355 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2362 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2363 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2364 if (changed && _preserve_fade_anchor) {
2365 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2367 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2368 framecnt_t len = ar->fade_out()->back()->when;
2369 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2370 framepos_t new_length = len + diff;
2371 i->anchored_fade_length = min (ar->length(), new_length);
2372 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2373 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2381 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2383 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2384 i->view->move_contents (frame_delta);
2390 switch (_operation) {
2392 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2395 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2398 // show_verbose_cursor_time (frame_delta);
2405 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2407 if (movement_occurred) {
2408 motion (event, false);
2410 if (_operation == StartTrim) {
2411 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2413 /* This must happen before the region's StatefulDiffCommand is created, as it may
2414 `correct' (ahem) the region's _start from being negative to being zero. It
2415 needs to be zero in the undo record.
2417 i->view->trim_front_ending ();
2419 if (_preserve_fade_anchor) {
2420 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2422 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2423 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2424 ar->set_fade_in_length(i->anchored_fade_length);
2425 ar->set_fade_in_active(true);
2428 if (_jump_position_when_done) {
2429 i->view->region()->set_position (i->initial_position);
2432 } else if (_operation == EndTrim) {
2433 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2434 if (_preserve_fade_anchor) {
2435 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2437 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2438 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2439 ar->set_fade_out_length(i->anchored_fade_length);
2440 ar->set_fade_out_active(true);
2443 if (_jump_position_when_done) {
2444 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2449 if (!_views.empty()) {
2450 if (_operation == StartTrim) {
2451 _editor->maybe_locate_with_edit_preroll(
2452 _views.begin()->view->region()->position());
2454 if (_operation == EndTrim) {
2455 _editor->maybe_locate_with_edit_preroll(
2456 _views.begin()->view->region()->position() +
2457 _views.begin()->view->region()->length());
2461 if (!_editor->selection->selected (_primary)) {
2462 _primary->thaw_after_trim ();
2465 set<boost::shared_ptr<Playlist> > diffed_playlists;
2467 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2468 i->view->thaw_after_trim ();
2469 i->view->enable_display (true);
2471 /* Trimming one region may affect others on the playlist, so we need
2472 to get undo Commands from the whole playlist rather than just the
2473 region. Use diffed_playlists to make sure we don't diff a given
2474 playlist more than once.
2476 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2477 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2478 vector<Command*> cmds;
2480 _editor->session()->add_commands (cmds);
2481 diffed_playlists.insert (p);
2486 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2490 _editor->motion_frozen_playlists.clear ();
2491 _editor->commit_reversible_command();
2494 /* no mouse movement */
2495 _editor->point_trim (event, adjusted_current_frame (event));
2498 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2499 if (_operation == StartTrim) {
2500 i->view->trim_front_ending ();
2503 i->view->region()->resume_property_changes ();
2508 TrimDrag::aborted (bool movement_occurred)
2510 /* Our motion method is changing model state, so use the Undo system
2511 to cancel. Perhaps not ideal, as this will leave an Undo point
2512 behind which may be slightly odd from the user's point of view.
2517 if (movement_occurred) {
2521 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2522 i->view->region()->resume_property_changes ();
2527 TrimDrag::setup_pointer_frame_offset ()
2529 list<DraggingView>::iterator i = _views.begin ();
2530 while (i != _views.end() && i->view != _primary) {
2534 if (i == _views.end()) {
2538 switch (_operation) {
2540 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2543 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2550 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2554 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2555 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2560 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2562 Drag::start_grab (event, cursor);
2563 show_verbose_cursor_time (adjusted_current_frame(event));
2567 MeterMarkerDrag::setup_pointer_frame_offset ()
2569 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2573 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2575 if (!_marker->meter().movable()) {
2581 // create a dummy marker for visual representation of moving the
2582 // section, because whether its a copy or not, we're going to
2583 // leave or lose the original marker (leave if its a copy; lose if its
2584 // not, because we'll remove it from the map).
2586 MeterSection section (_marker->meter());
2588 if (!section.movable()) {
2593 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2595 _marker = new MeterMarker (
2597 *_editor->meter_group,
2598 ARDOUR_UI::config()->get_MeterMarker(),
2600 *new MeterSection (_marker->meter())
2603 /* use the new marker for the grab */
2604 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2607 TempoMap& map (_editor->session()->tempo_map());
2608 /* get current state */
2609 before_state = &map.get_state();
2610 /* remove the section while we drag it */
2611 map.remove_meter (section, true);
2615 framepos_t const pf = adjusted_current_frame (event);
2616 _marker->set_position (pf);
2617 show_verbose_cursor_time (pf);
2621 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2623 if (!movement_occurred) {
2624 if (was_double_click()) {
2625 _editor->edit_meter_marker (*_marker);
2630 if (!_marker->meter().movable()) {
2634 motion (event, false);
2636 Timecode::BBT_Time when;
2638 TempoMap& map (_editor->session()->tempo_map());
2639 map.bbt_time (last_pointer_frame(), when);
2641 if (_copy == true) {
2642 _editor->begin_reversible_command (_("copy meter mark"));
2643 XMLNode &before = map.get_state();
2644 map.add_meter (_marker->meter(), when);
2645 XMLNode &after = map.get_state();
2646 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2647 _editor->commit_reversible_command ();
2650 _editor->begin_reversible_command (_("move meter mark"));
2652 /* we removed it before, so add it back now */
2654 map.add_meter (_marker->meter(), when);
2655 XMLNode &after = map.get_state();
2656 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2657 _editor->commit_reversible_command ();
2660 // delete the dummy marker we used for visual representation while moving.
2661 // a new visual marker will show up automatically.
2666 MeterMarkerDrag::aborted (bool moved)
2668 _marker->set_position (_marker->meter().frame ());
2671 TempoMap& map (_editor->session()->tempo_map());
2672 /* we removed it before, so add it back now */
2673 map.add_meter (_marker->meter(), _marker->meter().frame());
2674 // delete the dummy marker we used for visual representation while moving.
2675 // a new visual marker will show up automatically.
2680 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2684 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2686 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2691 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2693 Drag::start_grab (event, cursor);
2694 show_verbose_cursor_time (adjusted_current_frame (event));
2698 TempoMarkerDrag::setup_pointer_frame_offset ()
2700 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2704 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2706 if (!_marker->tempo().movable()) {
2712 // create a dummy marker for visual representation of moving the
2713 // section, because whether its a copy or not, we're going to
2714 // leave or lose the original marker (leave if its a copy; lose if its
2715 // not, because we'll remove it from the map).
2717 // create a dummy marker for visual representation of moving the copy.
2718 // The actual copying is not done before we reach the finish callback.
2721 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2723 TempoSection section (_marker->tempo());
2725 _marker = new TempoMarker (
2727 *_editor->tempo_group,
2728 ARDOUR_UI::config()->get_TempoMarker(),
2730 *new TempoSection (_marker->tempo())
2733 /* use the new marker for the grab */
2734 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2737 TempoMap& map (_editor->session()->tempo_map());
2738 /* get current state */
2739 before_state = &map.get_state();
2740 /* remove the section while we drag it */
2741 map.remove_tempo (section, true);
2745 framepos_t const pf = adjusted_current_frame (event);
2746 _marker->set_position (pf);
2747 show_verbose_cursor_time (pf);
2751 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2753 if (!movement_occurred) {
2754 if (was_double_click()) {
2755 _editor->edit_tempo_marker (*_marker);
2760 if (!_marker->tempo().movable()) {
2764 motion (event, false);
2766 TempoMap& map (_editor->session()->tempo_map());
2767 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
2768 Timecode::BBT_Time when;
2770 map.bbt_time (beat_time, when);
2772 if (_copy == true) {
2773 _editor->begin_reversible_command (_("copy tempo mark"));
2774 XMLNode &before = map.get_state();
2775 map.add_tempo (_marker->tempo(), when);
2776 XMLNode &after = map.get_state();
2777 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2778 _editor->commit_reversible_command ();
2781 _editor->begin_reversible_command (_("move tempo mark"));
2782 /* we removed it before, so add it back now */
2783 map.add_tempo (_marker->tempo(), when);
2784 XMLNode &after = map.get_state();
2785 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2786 _editor->commit_reversible_command ();
2789 // delete the dummy marker we used for visual representation while moving.
2790 // a new visual marker will show up automatically.
2795 TempoMarkerDrag::aborted (bool moved)
2797 _marker->set_position (_marker->tempo().frame());
2799 TempoMap& map (_editor->session()->tempo_map());
2800 /* we removed it before, so add it back now */
2801 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2802 // delete the dummy marker we used for visual representation while moving.
2803 // a new visual marker will show up automatically.
2808 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2809 : Drag (e, &c.track_canvas_item(), false)
2813 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2816 /** Do all the things we do when dragging the playhead to make it look as though
2817 * we have located, without actually doing the locate (because that would cause
2818 * the diskstream buffers to be refilled, which is too slow).
2821 CursorDrag::fake_locate (framepos_t t)
2823 _editor->playhead_cursor->set_position (t);
2825 Session* s = _editor->session ();
2826 if (s->timecode_transmission_suspended ()) {
2827 framepos_t const f = _editor->playhead_cursor->current_frame ();
2828 /* This is asynchronous so it will be sent "now"
2830 s->send_mmc_locate (f);
2831 /* These are synchronous and will be sent during the next
2834 s->queue_full_time_code ();
2835 s->queue_song_position_pointer ();
2838 show_verbose_cursor_time (t);
2839 _editor->UpdateAllTransportClocks (t);
2843 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2845 Drag::start_grab (event, c);
2847 _grab_zoom = _editor->samples_per_pixel;
2849 framepos_t where = _editor->canvas_event_sample (event);
2851 _editor->snap_to_with_modifier (where, event);
2853 _editor->_dragging_playhead = true;
2855 Session* s = _editor->session ();
2857 /* grab the track canvas item as well */
2859 _cursor.track_canvas_item().grab();
2862 if (_was_rolling && _stop) {
2866 if (s->is_auditioning()) {
2867 s->cancel_audition ();
2871 if (AudioEngine::instance()->connected()) {
2873 /* do this only if we're the engine is connected
2874 * because otherwise this request will never be
2875 * serviced and we'll busy wait forever. likewise,
2876 * notice if we are disconnected while waiting for the
2877 * request to be serviced.
2880 s->request_suspend_timecode_transmission ();
2881 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2882 /* twiddle our thumbs */
2887 fake_locate (where);
2891 CursorDrag::motion (GdkEvent* event, bool)
2893 framepos_t const adjusted_frame = adjusted_current_frame (event);
2894 if (adjusted_frame != last_pointer_frame()) {
2895 fake_locate (adjusted_frame);
2900 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2902 _editor->_dragging_playhead = false;
2904 _cursor.track_canvas_item().ungrab();
2906 if (!movement_occurred && _stop) {
2910 motion (event, false);
2912 Session* s = _editor->session ();
2914 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2915 _editor->_pending_locate_request = true;
2916 s->request_resume_timecode_transmission ();
2921 CursorDrag::aborted (bool)
2923 _cursor.track_canvas_item().ungrab();
2925 if (_editor->_dragging_playhead) {
2926 _editor->session()->request_resume_timecode_transmission ();
2927 _editor->_dragging_playhead = false;
2930 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2933 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2934 : RegionDrag (e, i, p, v)
2936 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2940 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2942 Drag::start_grab (event, cursor);
2944 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2945 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2947 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2951 FadeInDrag::setup_pointer_frame_offset ()
2953 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2954 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2955 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2959 FadeInDrag::motion (GdkEvent* event, bool)
2961 framecnt_t fade_length;
2963 framepos_t const pos = adjusted_current_frame (event);
2965 boost::shared_ptr<Region> region = _primary->region ();
2967 if (pos < (region->position() + 64)) {
2968 fade_length = 64; // this should be a minimum defined somewhere
2969 } else if (pos > region->last_frame()) {
2970 fade_length = region->length();
2972 fade_length = pos - region->position();
2975 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2977 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2983 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2986 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2990 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2992 if (!movement_occurred) {
2996 framecnt_t fade_length;
2998 framepos_t const pos = adjusted_current_frame (event);
3000 boost::shared_ptr<Region> region = _primary->region ();
3002 if (pos < (region->position() + 64)) {
3003 fade_length = 64; // this should be a minimum defined somewhere
3004 } else if (pos > region->last_frame()) {
3005 fade_length = region->length();
3007 fade_length = pos - region->position();
3010 _editor->begin_reversible_command (_("change fade in length"));
3012 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3014 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3020 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3021 XMLNode &before = alist->get_state();
3023 tmp->audio_region()->set_fade_in_length (fade_length);
3024 tmp->audio_region()->set_fade_in_active (true);
3026 XMLNode &after = alist->get_state();
3027 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3030 _editor->commit_reversible_command ();
3034 FadeInDrag::aborted (bool)
3036 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3037 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3043 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3047 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3048 : RegionDrag (e, i, p, v)
3050 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3054 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3056 Drag::start_grab (event, cursor);
3058 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3059 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3061 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3065 FadeOutDrag::setup_pointer_frame_offset ()
3067 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3068 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3069 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3073 FadeOutDrag::motion (GdkEvent* event, bool)
3075 framecnt_t fade_length;
3077 framepos_t const pos = adjusted_current_frame (event);
3079 boost::shared_ptr<Region> region = _primary->region ();
3081 if (pos > (region->last_frame() - 64)) {
3082 fade_length = 64; // this should really be a minimum fade defined somewhere
3084 else if (pos < region->position()) {
3085 fade_length = region->length();
3088 fade_length = region->last_frame() - pos;
3091 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3093 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3099 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3102 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3106 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3108 if (!movement_occurred) {
3112 framecnt_t fade_length;
3114 framepos_t const pos = adjusted_current_frame (event);
3116 boost::shared_ptr<Region> region = _primary->region ();
3118 if (pos > (region->last_frame() - 64)) {
3119 fade_length = 64; // this should really be a minimum fade defined somewhere
3121 else if (pos < region->position()) {
3122 fade_length = region->length();
3125 fade_length = region->last_frame() - pos;
3128 _editor->begin_reversible_command (_("change fade out length"));
3130 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3132 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3138 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3139 XMLNode &before = alist->get_state();
3141 tmp->audio_region()->set_fade_out_length (fade_length);
3142 tmp->audio_region()->set_fade_out_active (true);
3144 XMLNode &after = alist->get_state();
3145 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3148 _editor->commit_reversible_command ();
3152 FadeOutDrag::aborted (bool)
3154 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3155 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3161 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3165 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3168 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3170 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3173 _points.push_back (ArdourCanvas::Duple (0, 0));
3174 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3177 MarkerDrag::~MarkerDrag ()
3179 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3184 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3186 location = new Location (*l);
3187 markers.push_back (m);
3192 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3194 Drag::start_grab (event, cursor);
3198 Location *location = _editor->find_location_from_marker (_marker, is_start);
3199 _editor->_dragging_edit_point = true;
3201 update_item (location);
3203 // _drag_line->show();
3204 // _line->raise_to_top();
3207 show_verbose_cursor_time (location->start());
3209 show_verbose_cursor_time (location->end());
3212 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3215 case Selection::Toggle:
3216 /* we toggle on the button release */
3218 case Selection::Set:
3219 if (!_editor->selection->selected (_marker)) {
3220 _editor->selection->set (_marker);
3223 case Selection::Extend:
3225 Locations::LocationList ll;
3226 list<Marker*> to_add;
3228 _editor->selection->markers.range (s, e);
3229 s = min (_marker->position(), s);
3230 e = max (_marker->position(), e);
3233 if (e < max_framepos) {
3236 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3237 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3238 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3241 to_add.push_back (lm->start);
3244 to_add.push_back (lm->end);
3248 if (!to_add.empty()) {
3249 _editor->selection->add (to_add);
3253 case Selection::Add:
3254 _editor->selection->add (_marker);
3258 /* Set up copies for us to manipulate during the drag
3261 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3263 Location* l = _editor->find_location_from_marker (*i, is_start);
3270 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3272 /* range: check that the other end of the range isn't
3275 CopiedLocationInfo::iterator x;
3276 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3277 if (*(*x).location == *l) {
3281 if (x == _copied_locations.end()) {
3282 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3284 (*x).markers.push_back (*i);
3285 (*x).move_both = true;
3293 MarkerDrag::setup_pointer_frame_offset ()
3296 Location *location = _editor->find_location_from_marker (_marker, is_start);
3297 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3301 MarkerDrag::motion (GdkEvent* event, bool)
3303 framecnt_t f_delta = 0;
3305 bool move_both = false;
3306 Location *real_location;
3307 Location *copy_location = 0;
3309 framepos_t const newframe = adjusted_current_frame (event);
3310 framepos_t next = newframe;
3312 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3316 CopiedLocationInfo::iterator x;
3318 /* find the marker we're dragging, and compute the delta */
3320 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3322 copy_location = (*x).location;
3324 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3326 /* this marker is represented by this
3327 * CopiedLocationMarkerInfo
3330 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3335 if (real_location->is_mark()) {
3336 f_delta = newframe - copy_location->start();
3340 switch (_marker->type()) {
3341 case Marker::SessionStart:
3342 case Marker::RangeStart:
3343 case Marker::LoopStart:
3344 case Marker::PunchIn:
3345 f_delta = newframe - copy_location->start();
3348 case Marker::SessionEnd:
3349 case Marker::RangeEnd:
3350 case Marker::LoopEnd:
3351 case Marker::PunchOut:
3352 f_delta = newframe - copy_location->end();
3355 /* what kind of marker is this ? */
3364 if (x == _copied_locations.end()) {
3365 /* hmm, impossible - we didn't find the dragged marker */
3369 /* now move them all */
3371 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3373 copy_location = x->location;
3375 /* call this to find out if its the start or end */
3377 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3381 if (real_location->locked()) {
3385 if (copy_location->is_mark()) {
3389 copy_location->set_start (copy_location->start() + f_delta);
3393 framepos_t new_start = copy_location->start() + f_delta;
3394 framepos_t new_end = copy_location->end() + f_delta;
3396 if (is_start) { // start-of-range marker
3398 if (move_both || (*x).move_both) {
3399 copy_location->set_start (new_start);
3400 copy_location->set_end (new_end);
3401 } else if (new_start < copy_location->end()) {
3402 copy_location->set_start (new_start);
3403 } else if (newframe > 0) {
3404 _editor->snap_to (next, RoundUpAlways, true);
3405 copy_location->set_end (next);
3406 copy_location->set_start (newframe);
3409 } else { // end marker
3411 if (move_both || (*x).move_both) {
3412 copy_location->set_end (new_end);
3413 copy_location->set_start (new_start);
3414 } else if (new_end > copy_location->start()) {
3415 copy_location->set_end (new_end);
3416 } else if (newframe > 0) {
3417 _editor->snap_to (next, RoundDownAlways, true);
3418 copy_location->set_start (next);
3419 copy_location->set_end (newframe);
3424 update_item (copy_location);
3426 /* now lookup the actual GUI items used to display this
3427 * location and move them to wherever the copy of the location
3428 * is now. This means that the logic in ARDOUR::Location is
3429 * still enforced, even though we are not (yet) modifying
3430 * the real Location itself.
3433 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3436 lm->set_position (copy_location->start(), copy_location->end());
3441 assert (!_copied_locations.empty());
3443 show_verbose_cursor_time (newframe);
3447 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3449 if (!movement_occurred) {
3451 if (was_double_click()) {
3452 _editor->rename_marker (_marker);
3456 /* just a click, do nothing but finish
3457 off the selection process
3460 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3463 case Selection::Set:
3464 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3465 _editor->selection->set (_marker);
3469 case Selection::Toggle:
3470 /* we toggle on the button release, click only */
3471 _editor->selection->toggle (_marker);
3474 case Selection::Extend:
3475 case Selection::Add:
3482 _editor->_dragging_edit_point = false;
3484 _editor->begin_reversible_command ( _("move marker") );
3485 XMLNode &before = _editor->session()->locations()->get_state();
3487 MarkerSelection::iterator i;
3488 CopiedLocationInfo::iterator x;
3491 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3492 x != _copied_locations.end() && i != _editor->selection->markers.end();
3495 Location * location = _editor->find_location_from_marker (*i, is_start);
3499 if (location->locked()) {
3503 if (location->is_mark()) {
3504 location->set_start (((*x).location)->start());
3506 location->set (((*x).location)->start(), ((*x).location)->end());
3511 XMLNode &after = _editor->session()->locations()->get_state();
3512 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3513 _editor->commit_reversible_command ();
3517 MarkerDrag::aborted (bool)
3523 MarkerDrag::update_item (Location*)
3528 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3530 _cumulative_x_drag (0),
3531 _cumulative_y_drag (0)
3533 if (_zero_gain_fraction < 0.0) {
3534 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3537 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3539 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3545 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3547 Drag::start_grab (event, _editor->cursors()->fader);
3549 // start the grab at the center of the control point so
3550 // the point doesn't 'jump' to the mouse after the first drag
3551 _fixed_grab_x = _point->get_x();
3552 _fixed_grab_y = _point->get_y();
3554 float const fraction = 1 - (_point->get_y() / _point->line().height());
3556 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3558 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3560 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3562 if (!_point->can_slide ()) {
3563 _x_constrained = true;
3568 ControlPointDrag::motion (GdkEvent* event, bool)
3570 double dx = _drags->current_pointer_x() - last_pointer_x();
3571 double dy = current_pointer_y() - last_pointer_y();
3573 if (event->button.state & Keyboard::SecondaryModifier) {
3578 /* coordinate in pixels relative to the start of the region (for region-based automation)
3579 or track (for track-based automation) */
3580 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3581 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3583 // calculate zero crossing point. back off by .01 to stay on the
3584 // positive side of zero
3585 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3587 // make sure we hit zero when passing through
3588 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3592 if (_x_constrained) {
3595 if (_y_constrained) {
3599 _cumulative_x_drag = cx - _fixed_grab_x;
3600 _cumulative_y_drag = cy - _fixed_grab_y;
3604 cy = min ((double) _point->line().height(), cy);
3606 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3608 if (!_x_constrained) {
3609 _editor->snap_to_with_modifier (cx_frames, event);
3612 cx_frames = min (cx_frames, _point->line().maximum_time());
3614 float const fraction = 1.0 - (cy / _point->line().height());
3616 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3618 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3622 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3624 if (!movement_occurred) {
3628 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3629 _editor->reset_point_selection ();
3633 motion (event, false);
3636 _point->line().end_drag (_pushing, _final_index);
3637 _editor->session()->commit_reversible_command ();
3641 ControlPointDrag::aborted (bool)
3643 _point->line().reset ();
3647 ControlPointDrag::active (Editing::MouseMode m)
3649 if (m == Editing::MouseGain) {
3650 /* always active in mouse gain */
3654 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3655 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3658 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3661 _cumulative_y_drag (0)
3663 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3667 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3669 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3672 _item = &_line->grab_item ();
3674 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3675 origin, and ditto for y.
3678 double cx = event->button.x;
3679 double cy = event->button.y;
3681 _line->parent_group().canvas_to_item (cx, cy);
3683 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3688 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3689 /* no adjacent points */
3693 Drag::start_grab (event, _editor->cursors()->fader);
3695 /* store grab start in parent frame */
3700 double fraction = 1.0 - (cy / _line->height());
3702 _line->start_drag_line (before, after, fraction);
3704 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3708 LineDrag::motion (GdkEvent* event, bool)
3710 double dy = current_pointer_y() - last_pointer_y();
3712 if (event->button.state & Keyboard::SecondaryModifier) {
3716 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3718 _cumulative_y_drag = cy - _fixed_grab_y;
3721 cy = min ((double) _line->height(), cy);
3723 double const fraction = 1.0 - (cy / _line->height());
3726 /* we are ignoring x position for this drag, so we can just pass in anything */
3727 _line->drag_motion (0, fraction, true, false, ignored);
3729 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3733 LineDrag::finished (GdkEvent* event, bool movement_occured)
3735 if (movement_occured) {
3736 motion (event, false);
3737 _line->end_drag (false, 0);
3739 /* add a new control point on the line */
3741 AutomationTimeAxisView* atv;
3743 _line->end_drag (false, 0);
3745 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3746 framepos_t where = _editor->window_event_sample (event, 0, 0);
3747 atv->add_automation_event (event, where, event->button.y, false);
3751 _editor->session()->commit_reversible_command ();
3755 LineDrag::aborted (bool)
3760 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3763 _cumulative_x_drag (0)
3765 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3769 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3771 Drag::start_grab (event);
3773 _line = reinterpret_cast<Line*> (_item);
3776 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3778 double cx = event->button.x;
3779 double cy = event->button.y;
3781 _item->parent()->canvas_to_item (cx, cy);
3783 /* store grab start in parent frame */
3784 _region_view_grab_x = cx;
3786 _before = *(float*) _item->get_data ("position");
3788 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3790 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3794 FeatureLineDrag::motion (GdkEvent*, bool)
3796 double dx = _drags->current_pointer_x() - last_pointer_x();
3798 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3800 _cumulative_x_drag += dx;
3802 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3811 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3813 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3815 float *pos = new float;
3818 _line->set_data ("position", pos);
3824 FeatureLineDrag::finished (GdkEvent*, bool)
3826 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3827 _arv->update_transient(_before, _before);
3831 FeatureLineDrag::aborted (bool)
3836 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3838 , _vertical_only (false)
3840 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3844 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3846 Drag::start_grab (event);
3847 show_verbose_cursor_time (adjusted_current_frame (event));
3851 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3858 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3860 framepos_t grab = grab_frame ();
3861 if (Config->get_rubberbanding_snaps_to_grid ()) {
3862 _editor->snap_to_with_modifier (grab, event);
3865 /* base start and end on initial click position */
3875 if (current_pointer_y() < grab_y()) {
3876 y1 = current_pointer_y();
3879 y2 = current_pointer_y();
3883 if (start != end || y1 != y2) {
3885 double x1 = _editor->sample_to_pixel (start);
3886 double x2 = _editor->sample_to_pixel (end);
3887 const double min_dimension = 2.0;
3889 if (_vertical_only) {
3890 /* fixed 10 pixel width */
3894 x2 = min (x1 - min_dimension, x2);
3896 x2 = max (x1 + min_dimension, x2);
3901 y2 = min (y1 - min_dimension, y2);
3903 y2 = max (y1 + min_dimension, y2);
3906 /* translate rect into item space and set */
3908 ArdourCanvas::Rect r (x1, y1, x2, y2);
3910 /* this drag is a _trackview_only == true drag, so the y1 and
3911 * y2 (computed using current_pointer_y() and grab_y()) will be
3912 * relative to the top of the trackview group). The
3913 * rubberband rect has the same parent/scroll offset as the
3914 * the trackview group, so we can use the "r" rect directly
3915 * to set the shape of the rubberband.
3918 _editor->rubberband_rect->set (r);
3919 _editor->rubberband_rect->show();
3920 _editor->rubberband_rect->raise_to_top();
3922 show_verbose_cursor_time (pf);
3924 do_select_things (event, true);
3929 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3934 if (grab_frame() < last_pointer_frame()) {
3936 x2 = last_pointer_frame ();
3939 x1 = last_pointer_frame ();
3945 if (current_pointer_y() < grab_y()) {
3946 y1 = current_pointer_y();
3949 y2 = current_pointer_y();
3953 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3957 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3959 if (movement_occurred) {
3961 motion (event, false);
3962 do_select_things (event, false);
3968 bool do_deselect = true;
3969 MidiTimeAxisView* mtv;
3971 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3973 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
3974 /* nothing selected */
3975 add_midi_region (mtv);
3976 do_deselect = false;
3980 /* do not deselect if Primary or Tertiary (toggle-select or
3981 * extend-select are pressed.
3984 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3985 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3992 _editor->rubberband_rect->hide();
3996 RubberbandSelectDrag::aborted (bool)
3998 _editor->rubberband_rect->hide ();
4001 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4002 : RegionDrag (e, i, p, v)
4004 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4008 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4010 Drag::start_grab (event, cursor);
4012 show_verbose_cursor_time (adjusted_current_frame (event));
4016 TimeFXDrag::motion (GdkEvent* event, bool)
4018 RegionView* rv = _primary;
4019 StreamView* cv = rv->get_time_axis_view().view ();
4021 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4022 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4023 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4025 framepos_t const pf = adjusted_current_frame (event);
4027 if (pf > rv->region()->position()) {
4028 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4031 show_verbose_cursor_time (pf);
4035 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4037 _primary->get_time_axis_view().hide_timestretch ();
4039 if (!movement_occurred) {
4043 if (last_pointer_frame() < _primary->region()->position()) {
4044 /* backwards drag of the left edge - not usable */
4048 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4050 float percentage = (double) newlen / (double) _primary->region()->length();
4052 #ifndef USE_RUBBERBAND
4053 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4054 if (_primary->region()->data_type() == DataType::AUDIO) {
4055 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4059 if (!_editor->get_selection().regions.empty()) {
4060 /* primary will already be included in the selection, and edit
4061 group shared editing will propagate selection across
4062 equivalent regions, so just use the current region
4066 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4067 error << _("An error occurred while executing time stretch operation") << endmsg;
4073 TimeFXDrag::aborted (bool)
4075 _primary->get_time_axis_view().hide_timestretch ();
4078 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4081 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4085 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4087 Drag::start_grab (event);
4091 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4093 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4097 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4099 if (movement_occurred && _editor->session()) {
4100 /* make sure we stop */
4101 _editor->session()->request_transport_speed (0.0);
4106 ScrubDrag::aborted (bool)
4111 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4116 , _original_pointer_time_axis (-1)
4117 , _last_pointer_time_axis (-1)
4118 , _time_selection_at_start (!_editor->get_selection().time.empty())
4120 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4122 if (_time_selection_at_start) {
4123 start_at_start = _editor->get_selection().time.start();
4124 end_at_start = _editor->get_selection().time.end_frame();
4129 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4131 if (_editor->session() == 0) {
4135 Gdk::Cursor* cursor = 0;
4137 switch (_operation) {
4138 case CreateSelection:
4139 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4144 cursor = _editor->cursors()->selector;
4145 Drag::start_grab (event, cursor);
4148 case SelectionStartTrim:
4149 if (_editor->clicked_axisview) {
4150 _editor->clicked_axisview->order_selection_trims (_item, true);
4152 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4155 case SelectionEndTrim:
4156 if (_editor->clicked_axisview) {
4157 _editor->clicked_axisview->order_selection_trims (_item, false);
4159 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4163 Drag::start_grab (event, cursor);
4166 case SelectionExtend:
4167 Drag::start_grab (event, cursor);
4171 if (_operation == SelectionMove) {
4172 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4174 show_verbose_cursor_time (adjusted_current_frame (event));
4177 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4181 SelectionDrag::setup_pointer_frame_offset ()
4183 switch (_operation) {
4184 case CreateSelection:
4185 _pointer_frame_offset = 0;
4188 case SelectionStartTrim:
4190 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4193 case SelectionEndTrim:
4194 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4197 case SelectionExtend:
4203 SelectionDrag::motion (GdkEvent* event, bool first_move)
4205 framepos_t start = 0;
4207 framecnt_t length = 0;
4208 framecnt_t distance = 0;
4210 framepos_t const pending_position = adjusted_current_frame (event);
4212 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4216 switch (_operation) {
4217 case CreateSelection:
4219 framepos_t grab = grab_frame ();
4222 grab = adjusted_current_frame (event, false);
4223 if (grab < pending_position) {
4224 _editor->snap_to (grab, RoundDownMaybe);
4226 _editor->snap_to (grab, RoundUpMaybe);
4230 if (pending_position < grab) {
4231 start = pending_position;
4234 end = pending_position;
4238 /* first drag: Either add to the selection
4239 or create a new selection
4246 /* adding to the selection */
4247 _editor->set_selected_track_as_side_effect (Selection::Add);
4248 _editor->clicked_selection = _editor->selection->add (start, end);
4255 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4256 _editor->set_selected_track_as_side_effect (Selection::Set);
4259 _editor->clicked_selection = _editor->selection->set (start, end);
4263 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4264 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4265 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4267 _editor->selection->add (atest);
4271 /* select all tracks within the rectangle that we've marked out so far */
4272 TrackViewList new_selection;
4273 TrackViewList& all_tracks (_editor->track_views);
4275 ArdourCanvas::Coord const top = grab_y();
4276 ArdourCanvas::Coord const bottom = current_pointer_y();
4278 if (top >= 0 && bottom >= 0) {
4280 //first, find the tracks that are covered in the y range selection
4281 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4282 if ((*i)->covered_by_y_range (top, bottom)) {
4283 new_selection.push_back (*i);
4287 //now find any tracks that are GROUPED with the tracks we selected
4288 TrackViewList grouped_add = new_selection;
4289 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4290 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4291 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4292 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4293 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4294 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4295 grouped_add.push_back (*j);
4300 //now compare our list with the current selection, and add or remove as necessary
4301 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4302 TrackViewList tracks_to_add;
4303 TrackViewList tracks_to_remove;
4304 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4305 if ( !_editor->selection->tracks.contains ( *i ) )
4306 tracks_to_add.push_back ( *i );
4307 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4308 if ( !grouped_add.contains ( *i ) )
4309 tracks_to_remove.push_back ( *i );
4310 _editor->selection->add(tracks_to_add);
4311 _editor->selection->remove(tracks_to_remove);
4317 case SelectionStartTrim:
4319 start = _editor->selection->time[_editor->clicked_selection].start;
4320 end = _editor->selection->time[_editor->clicked_selection].end;
4322 if (pending_position > end) {
4325 start = pending_position;
4329 case SelectionEndTrim:
4331 start = _editor->selection->time[_editor->clicked_selection].start;
4332 end = _editor->selection->time[_editor->clicked_selection].end;
4334 if (pending_position < start) {
4337 end = pending_position;
4344 start = _editor->selection->time[_editor->clicked_selection].start;
4345 end = _editor->selection->time[_editor->clicked_selection].end;
4347 length = end - start;
4348 distance = pending_position - start;
4349 start = pending_position;
4350 _editor->snap_to (start);
4352 end = start + length;
4356 case SelectionExtend:
4361 switch (_operation) {
4363 if (_time_selection_at_start) {
4364 _editor->selection->move_time (distance);
4368 _editor->selection->replace (_editor->clicked_selection, start, end);
4372 if (_operation == SelectionMove) {
4373 show_verbose_cursor_time(start);
4375 show_verbose_cursor_time(pending_position);
4380 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4382 Session* s = _editor->session();
4384 if (movement_occurred) {
4385 motion (event, false);
4386 /* XXX this is not object-oriented programming at all. ick */
4387 if (_editor->selection->time.consolidate()) {
4388 _editor->selection->TimeChanged ();
4391 /* XXX what if its a music time selection? */
4393 if ( s->get_play_range() && s->transport_rolling() ) {
4394 s->request_play_range (&_editor->selection->time, true);
4396 if (Config->get_follow_edits() && !s->transport_rolling()) {
4397 if (_operation == SelectionEndTrim)
4398 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4400 s->request_locate (_editor->get_selection().time.start());
4406 /* just a click, no pointer movement.
4409 if (_operation == SelectionExtend) {
4410 if (_time_selection_at_start) {
4411 framepos_t pos = adjusted_current_frame (event, false);
4412 framepos_t start = min (pos, start_at_start);
4413 framepos_t end = max (pos, end_at_start);
4414 _editor->selection->set (start, end);
4417 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4418 if (_editor->clicked_selection) {
4419 _editor->selection->remove (_editor->clicked_selection);
4422 if (!_editor->clicked_selection) {
4423 _editor->selection->clear_time();
4428 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4429 _editor->selection->set (_editor->clicked_axisview);
4432 if (s && s->get_play_range () && s->transport_rolling()) {
4433 s->request_stop (false, false);
4438 _editor->stop_canvas_autoscroll ();
4439 _editor->clicked_selection = 0;
4443 SelectionDrag::aborted (bool)
4448 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4449 : Drag (e, i, false),
4453 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4455 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4456 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4457 physical_screen_height (_editor->get_window())));
4458 _drag_rect->hide ();
4460 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_RangeDragRect());
4461 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_RangeDragRect());
4465 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4467 if (_editor->session() == 0) {
4471 Gdk::Cursor* cursor = 0;
4473 if (!_editor->temp_location) {
4474 _editor->temp_location = new Location (*_editor->session());
4477 switch (_operation) {
4478 case CreateSkipMarker:
4479 case CreateRangeMarker:
4480 case CreateTransportMarker:
4481 case CreateCDMarker:
4483 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4488 cursor = _editor->cursors()->selector;
4492 Drag::start_grab (event, cursor);
4494 show_verbose_cursor_time (adjusted_current_frame (event));
4498 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4500 framepos_t start = 0;
4502 ArdourCanvas::Rectangle *crect;
4504 switch (_operation) {
4505 case CreateSkipMarker:
4506 crect = _editor->range_bar_drag_rect;
4508 case CreateRangeMarker:
4509 crect = _editor->range_bar_drag_rect;
4511 case CreateTransportMarker:
4512 crect = _editor->transport_bar_drag_rect;
4514 case CreateCDMarker:
4515 crect = _editor->cd_marker_bar_drag_rect;
4518 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4523 framepos_t const pf = adjusted_current_frame (event);
4525 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4526 framepos_t grab = grab_frame ();
4527 _editor->snap_to (grab);
4529 if (pf < grab_frame()) {
4537 /* first drag: Either add to the selection
4538 or create a new selection.
4543 _editor->temp_location->set (start, end);
4547 update_item (_editor->temp_location);
4549 //_drag_rect->raise_to_top();
4555 _editor->temp_location->set (start, end);
4557 double x1 = _editor->sample_to_pixel (start);
4558 double x2 = _editor->sample_to_pixel (end);
4562 update_item (_editor->temp_location);
4565 show_verbose_cursor_time (pf);
4570 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4572 Location * newloc = 0;
4576 if (movement_occurred) {
4577 motion (event, false);
4580 switch (_operation) {
4581 case CreateSkipMarker:
4582 case CreateRangeMarker:
4583 case CreateCDMarker:
4585 XMLNode &before = _editor->session()->locations()->get_state();
4586 if (_operation == CreateSkipMarker) {
4587 _editor->begin_reversible_command (_("new skip marker"));
4588 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4589 flags = Location::IsRangeMarker | Location::IsSkip;
4590 _editor->range_bar_drag_rect->hide();
4591 } else if (_operation == CreateCDMarker) {
4592 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4593 _editor->begin_reversible_command (_("new CD marker"));
4594 flags = Location::IsRangeMarker | Location::IsCDMarker;
4595 _editor->cd_marker_bar_drag_rect->hide();
4597 _editor->begin_reversible_command (_("new skip marker"));
4598 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4599 flags = Location::IsRangeMarker;
4600 _editor->range_bar_drag_rect->hide();
4602 newloc = new Location (
4603 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4606 _editor->session()->locations()->add (newloc, true);
4607 XMLNode &after = _editor->session()->locations()->get_state();
4608 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4609 _editor->commit_reversible_command ();
4613 case CreateTransportMarker:
4614 // popup menu to pick loop or punch
4615 _editor->new_transport_marker_context_menu (&event->button, _item);
4621 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4623 if (_operation == CreateTransportMarker) {
4625 /* didn't drag, so just locate */
4627 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4629 } else if (_operation == CreateCDMarker) {
4631 /* didn't drag, but mark is already created so do
4634 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
4640 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4642 if (end == max_framepos) {
4643 end = _editor->session()->current_end_frame ();
4646 if (start == max_framepos) {
4647 start = _editor->session()->current_start_frame ();
4650 switch (_editor->mouse_mode) {
4652 /* find the two markers on either side and then make the selection from it */
4653 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4657 /* find the two markers on either side of the click and make the range out of it */
4658 _editor->selection->set (start, end);
4667 _editor->stop_canvas_autoscroll ();
4671 RangeMarkerBarDrag::aborted (bool)
4677 RangeMarkerBarDrag::update_item (Location* location)
4679 double const x1 = _editor->sample_to_pixel (location->start());
4680 double const x2 = _editor->sample_to_pixel (location->end());
4682 _drag_rect->set_x0 (x1);
4683 _drag_rect->set_x1 (x2);
4686 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4688 , _cumulative_dx (0)
4689 , _cumulative_dy (0)
4691 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4693 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4695 _region = &_primary->region_view ();
4696 _note_height = _region->midi_stream_view()->note_height ();
4700 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4702 Drag::start_grab (event);
4704 if (!(_was_selected = _primary->selected())) {
4706 /* tertiary-click means extend selection - we'll do that on button release,
4707 so don't add it here, because otherwise we make it hard to figure
4708 out the "extend-to" range.
4711 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4714 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4717 _region->note_selected (_primary, true);
4719 _region->unique_select (_primary);
4725 /** @return Current total drag x change in frames */
4727 NoteDrag::total_dx () const
4730 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4732 /* primary note time */
4733 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4735 /* new time of the primary note in session frames */
4736 frameoffset_t st = n + dx;
4738 framepos_t const rp = _region->region()->position ();
4740 /* prevent the note being dragged earlier than the region's position */
4743 /* snap and return corresponding delta */
4744 return _region->snap_frame_to_frame (st - rp) + rp - n;
4747 /** @return Current total drag y change in note number */
4749 NoteDrag::total_dy () const
4751 MidiStreamView* msv = _region->midi_stream_view ();
4752 double const y = _region->midi_view()->y_position ();
4753 /* new current note */
4754 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4756 n = max (msv->lowest_note(), n);
4757 n = min (msv->highest_note(), n);
4758 /* and work out delta */
4759 return n - msv->y_to_note (grab_y() - y);
4763 NoteDrag::motion (GdkEvent *, bool)
4765 /* Total change in x and y since the start of the drag */
4766 frameoffset_t const dx = total_dx ();
4767 int8_t const dy = total_dy ();
4769 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4770 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4771 double const tdy = -dy * _note_height - _cumulative_dy;
4774 _cumulative_dx += tdx;
4775 _cumulative_dy += tdy;
4777 int8_t note_delta = total_dy();
4779 _region->move_selection (tdx, tdy, note_delta);
4781 /* the new note value may be the same as the old one, but we
4782 * don't know what that means because the selection may have
4783 * involved more than one note and we might be doing something
4784 * odd with them. so show the note value anyway, always.
4788 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4790 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4791 (int) floor ((double)new_note));
4793 show_verbose_cursor_text (buf);
4798 NoteDrag::finished (GdkEvent* ev, bool moved)
4801 /* no motion - select note */
4803 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4804 _editor->current_mouse_mode() == Editing::MouseDraw) {
4806 if (_was_selected) {
4807 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4809 _region->note_deselected (_primary);
4812 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4813 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4815 if (!extend && !add && _region->selection_size() > 1) {
4816 _region->unique_select (_primary);
4817 } else if (extend) {
4818 _region->note_selected (_primary, true, true);
4820 /* it was added during button press */
4825 _region->note_dropped (_primary, total_dx(), total_dy());
4830 NoteDrag::aborted (bool)
4835 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4836 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4837 : Drag (editor, atv->base_item ())
4839 , _nothing_to_drag (false)
4841 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4842 y_origin = atv->y_position();
4843 setup (atv->lines ());
4846 /** Make an AutomationRangeDrag for region gain lines */
4847 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4848 : Drag (editor, rv->get_canvas_group ())
4850 , _nothing_to_drag (false)
4852 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4854 list<boost::shared_ptr<AutomationLine> > lines;
4855 lines.push_back (rv->get_gain_line ());
4856 y_origin = rv->get_time_axis_view().y_position();
4860 /** @param lines AutomationLines to drag.
4861 * @param offset Offset from the session start to the points in the AutomationLines.
4864 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4866 /* find the lines that overlap the ranges being dragged */
4867 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4868 while (i != lines.end ()) {
4869 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4872 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4874 /* check this range against all the AudioRanges that we are using */
4875 list<AudioRange>::const_iterator k = _ranges.begin ();
4876 while (k != _ranges.end()) {
4877 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4883 /* add it to our list if it overlaps at all */
4884 if (k != _ranges.end()) {
4889 _lines.push_back (n);
4895 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4899 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4901 return 1.0 - ((global_y - y_origin) / line->height());
4905 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4907 Drag::start_grab (event, cursor);
4909 /* Get line states before we start changing things */
4910 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4911 i->state = &i->line->get_state ();
4912 i->original_fraction = y_fraction (i->line, current_pointer_y());
4915 if (_ranges.empty()) {
4917 /* No selected time ranges: drag all points */
4918 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4919 uint32_t const N = i->line->npoints ();
4920 for (uint32_t j = 0; j < N; ++j) {
4921 i->points.push_back (i->line->nth (j));
4927 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4929 framecnt_t const half = (i->start + i->end) / 2;
4931 /* find the line that this audio range starts in */
4932 list<Line>::iterator j = _lines.begin();
4933 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4937 if (j != _lines.end()) {
4938 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4940 /* j is the line that this audio range starts in; fade into it;
4941 64 samples length plucked out of thin air.
4944 framepos_t a = i->start + 64;
4949 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4950 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4952 the_list->editor_add (p, the_list->eval (p));
4953 the_list->editor_add (q, the_list->eval (q));
4956 /* same thing for the end */
4959 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4963 if (j != _lines.end()) {
4964 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4966 /* j is the line that this audio range starts in; fade out of it;
4967 64 samples length plucked out of thin air.
4970 framepos_t b = i->end - 64;
4975 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4976 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4978 the_list->editor_add (p, the_list->eval (p));
4979 the_list->editor_add (q, the_list->eval (q));
4983 _nothing_to_drag = true;
4985 /* Find all the points that should be dragged and put them in the relevant
4986 points lists in the Line structs.
4989 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4991 uint32_t const N = i->line->npoints ();
4992 for (uint32_t j = 0; j < N; ++j) {
4994 /* here's a control point on this line */
4995 ControlPoint* p = i->line->nth (j);
4996 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4998 /* see if it's inside a range */
4999 list<AudioRange>::const_iterator k = _ranges.begin ();
5000 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5004 if (k != _ranges.end()) {
5005 /* dragging this point */
5006 _nothing_to_drag = false;
5007 i->points.push_back (p);
5013 if (_nothing_to_drag) {
5017 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5018 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5023 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5025 if (_nothing_to_drag) {
5029 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5030 float const f = y_fraction (l->line, current_pointer_y());
5031 /* we are ignoring x position for this drag, so we can just pass in anything */
5033 l->line->drag_motion (0, f, true, false, ignored);
5034 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5039 AutomationRangeDrag::finished (GdkEvent* event, bool)
5041 if (_nothing_to_drag) {
5045 motion (event, false);
5046 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5047 i->line->end_drag (false, 0);
5050 _editor->session()->commit_reversible_command ();
5054 AutomationRangeDrag::aborted (bool)
5056 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5061 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5063 , initial_time_axis_view (itav)
5065 /* note that time_axis_view may be null if the regionview was created
5066 * as part of a copy operation.
5068 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5069 layer = v->region()->layer ();
5070 initial_y = v->get_canvas_group()->position().y;
5071 initial_playlist = v->region()->playlist ();
5072 initial_position = v->region()->position ();
5073 initial_end = v->region()->position () + v->region()->length ();
5076 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5077 : Drag (e, i->canvas_item ())
5080 , _cumulative_dx (0)
5082 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5083 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5088 PatchChangeDrag::motion (GdkEvent* ev, bool)
5090 framepos_t f = adjusted_current_frame (ev);
5091 boost::shared_ptr<Region> r = _region_view->region ();
5092 f = max (f, r->position ());
5093 f = min (f, r->last_frame ());
5095 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5096 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5097 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5098 _cumulative_dx = dxu;
5102 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5104 if (!movement_occurred) {
5108 boost::shared_ptr<Region> r (_region_view->region ());
5109 framepos_t f = adjusted_current_frame (ev);
5110 f = max (f, r->position ());
5111 f = min (f, r->last_frame ());
5113 _region_view->move_patch_change (
5115 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5120 PatchChangeDrag::aborted (bool)
5122 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5126 PatchChangeDrag::setup_pointer_frame_offset ()
5128 boost::shared_ptr<Region> region = _region_view->region ();
5129 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5132 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5133 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5140 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5142 framepos_t const p = _region_view->region()->position ();
5143 double const y = _region_view->midi_view()->y_position ();
5145 x1 = max ((framepos_t) 0, x1 - p);
5146 x2 = max ((framepos_t) 0, x2 - p);
5147 y1 = max (0.0, y1 - y);
5148 y2 = max (0.0, y2 - y);
5150 _region_view->update_drag_selection (
5151 _editor->sample_to_pixel (x1),
5152 _editor->sample_to_pixel (x2),
5155 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5160 MidiRubberbandSelectDrag::deselect_things ()
5165 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5166 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5169 _vertical_only = true;
5173 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5175 double const y = _region_view->midi_view()->y_position ();
5177 y1 = max (0.0, y1 - y);
5178 y2 = max (0.0, y2 - y);
5180 _region_view->update_vertical_drag_selection (
5183 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5188 MidiVerticalSelectDrag::deselect_things ()
5193 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5194 : RubberbandSelectDrag (e, i)
5200 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5202 if (drag_in_progress) {
5203 /* We just want to select things at the end of the drag, not during it */
5207 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5209 _editor->begin_reversible_command (_("rubberband selection"));
5210 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5211 _editor->commit_reversible_command ();
5215 EditorRubberbandSelectDrag::deselect_things ()
5217 if (!getenv("ARDOUR_SAE")) {
5218 _editor->selection->clear_tracks();
5220 _editor->selection->clear_regions();
5221 _editor->selection->clear_points ();
5222 _editor->selection->clear_lines ();
5225 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5233 NoteCreateDrag::~NoteCreateDrag ()
5239 NoteCreateDrag::grid_frames (framepos_t t) const
5242 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
5247 return _region_view->region_beats_to_region_frames (grid_beats);
5251 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5253 Drag::start_grab (event, cursor);
5255 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5257 framepos_t pf = _drags->current_pointer_frame ();
5258 framecnt_t const g = grid_frames (pf);
5260 /* Hack so that we always snap to the note that we are over, instead of snapping
5261 to the next one if we're more than halfway through the one we're over.
5263 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5267 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5269 MidiStreamView* sv = _region_view->midi_stream_view ();
5270 double const x = _editor->sample_to_pixel (_note[0]);
5271 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5273 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5274 _drag_rect->set_outline_all ();
5275 _drag_rect->set_outline_color (0xffffff99);
5276 _drag_rect->set_fill_color (0xffffff66);
5280 NoteCreateDrag::motion (GdkEvent* event, bool)
5282 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5283 double const x0 = _editor->sample_to_pixel (_note[0]);
5284 double const x1 = _editor->sample_to_pixel (_note[1]);
5285 _drag_rect->set_x0 (std::min(x0, x1));
5286 _drag_rect->set_x1 (std::max(x0, x1));
5290 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5292 if (!had_movement) {
5296 framepos_t const start = min (_note[0], _note[1]);
5297 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5299 framecnt_t const g = grid_frames (start);
5300 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5302 if (_editor->snap_mode() == SnapNormal && length < g) {
5303 length = g - one_tick;
5306 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5308 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5312 NoteCreateDrag::y_to_region (double y) const
5315 _region_view->get_canvas_group()->canvas_to_item (x, y);
5320 NoteCreateDrag::aborted (bool)
5325 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5330 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5334 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5336 Drag::start_grab (event, cursor);
5340 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5346 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5349 distance = _drags->current_pointer_x() - grab_x();
5350 len = ar->fade_in()->back()->when;
5352 distance = grab_x() - _drags->current_pointer_x();
5353 len = ar->fade_out()->back()->when;
5356 /* how long should it be ? */
5358 new_length = len + _editor->pixel_to_sample (distance);
5360 /* now check with the region that this is legal */
5362 new_length = ar->verify_xfade_bounds (new_length, start);
5365 arv->reset_fade_in_shape_width (ar, new_length);
5367 arv->reset_fade_out_shape_width (ar, new_length);
5372 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5378 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5381 distance = _drags->current_pointer_x() - grab_x();
5382 len = ar->fade_in()->back()->when;
5384 distance = grab_x() - _drags->current_pointer_x();
5385 len = ar->fade_out()->back()->when;
5388 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5390 _editor->begin_reversible_command ("xfade trim");
5391 ar->playlist()->clear_owned_changes ();
5394 ar->set_fade_in_length (new_length);
5396 ar->set_fade_out_length (new_length);
5399 /* Adjusting the xfade may affect other regions in the playlist, so we need
5400 to get undo Commands from the whole playlist rather than just the
5404 vector<Command*> cmds;
5405 ar->playlist()->rdiff (cmds);
5406 _editor->session()->add_commands (cmds);
5407 _editor->commit_reversible_command ();
5412 CrossfadeEdgeDrag::aborted (bool)
5415 arv->redraw_start_xfade ();
5417 arv->redraw_end_xfade ();
5421 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5422 : Drag (e, item, true)
5423 , line (new EditorCursor (*e))
5425 line->set_position (pos);
5429 RegionCutDrag::~RegionCutDrag ()
5435 RegionCutDrag::motion (GdkEvent*, bool)
5437 framepos_t where = _drags->current_pointer_frame();
5438 _editor->snap_to (where);
5440 line->set_position (where);
5444 RegionCutDrag::finished (GdkEvent*, bool)
5446 _editor->get_track_canvas()->canvas()->re_enter();
5448 framepos_t pos = _drags->current_pointer_frame();
5452 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5458 _editor->split_regions_at (pos, rs);
5462 RegionCutDrag::aborted (bool)