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/dB.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/operations.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/session.h"
44 #include "audio_region_view.h"
45 #include "midi_region_view.h"
46 #include "ardour_ui.h"
47 #include "gui_thread.h"
48 #include "control_point.h"
50 #include "region_gain_line.h"
51 #include "editor_drag.h"
52 #include "audio_time_axis.h"
53 #include "midi_time_axis.h"
54 #include "selection.h"
55 #include "midi_selection.h"
56 #include "automation_time_axis.h"
58 #include "editor_cursors.h"
59 #include "mouse_cursors.h"
60 #include "note_base.h"
61 #include "patch_change.h"
62 #include "verbose_cursor.h"
65 using namespace ARDOUR;
68 using namespace Gtkmm2ext;
69 using namespace Editing;
70 using namespace ArdourCanvas;
72 using Gtkmm2ext::Keyboard;
74 double ControlPointDrag::_zero_gain_fraction = -1.0;
76 DragManager::DragManager (Editor* e)
79 , _current_pointer_frame (0)
83 DragManager::~DragManager ()
88 /** Call abort for each active drag */
94 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
99 if (!_drags.empty ()) {
100 _editor->set_follow_playhead (_old_follow_playhead, false);
109 DragManager::add (Drag* d)
111 d->set_manager (this);
112 _drags.push_back (d);
116 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
118 d->set_manager (this);
119 _drags.push_back (d);
124 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
126 /* Prevent follow playhead during the drag to be nice to the user */
127 _old_follow_playhead = _editor->follow_playhead ();
128 _editor->set_follow_playhead (false);
130 _current_pointer_frame = _editor->canvas_event_frame (e, &_current_pointer_x, &_current_pointer_y);
132 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
133 (*i)->start_grab (e, c);
137 /** Call end_grab for each active drag.
138 * @return true if any drag reported movement having occurred.
141 DragManager::end_grab (GdkEvent* e)
146 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
147 bool const t = (*i)->end_grab (e);
158 _editor->set_follow_playhead (_old_follow_playhead, false);
164 DragManager::mark_double_click ()
166 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
167 (*i)->set_double_click (true);
172 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
176 _current_pointer_frame = _editor->canvas_event_frame (e, &_current_pointer_x, &_current_pointer_y);
178 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
179 bool const t = (*i)->motion_handler (e, from_autoscroll);
190 DragManager::window_motion_handler (GdkEvent* e, bool from_autoscroll)
194 _current_pointer_frame = _editor->window_event_frame (e, &_current_pointer_x, &_current_pointer_y);
196 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
197 bool const t = (*i)->motion_handler (e, from_autoscroll);
208 DragManager::have_item (ArdourCanvas::Item* i) const
210 list<Drag*>::const_iterator j = _drags.begin ();
211 while (j != _drags.end() && (*j)->item () != i) {
215 return j != _drags.end ();
218 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
221 , _pointer_frame_offset (0)
222 , _move_threshold_passed (false)
223 , _was_double_click (false)
224 , _raw_grab_frame (0)
226 , _last_pointer_frame (0)
232 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
245 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
247 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
249 if (Keyboard::is_button2_event (&event->button)) {
250 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
251 _y_constrained = true;
252 _x_constrained = false;
254 _y_constrained = false;
255 _x_constrained = true;
258 _x_constrained = false;
259 _y_constrained = false;
262 _raw_grab_frame = _editor->canvas_event_frame (event, &_grab_x, &_grab_y);
263 setup_pointer_frame_offset ();
264 _grab_frame = adjusted_frame (_raw_grab_frame, event);
265 _last_pointer_frame = _grab_frame;
266 _last_pointer_x = _grab_x;
267 _last_pointer_y = _grab_y;
273 /* CAIROCANVAS need a variant here that passes *cursor */
278 if (_editor->session() && _editor->session()->transport_rolling()) {
281 _was_rolling = false;
284 switch (_editor->snap_type()) {
285 case SnapToRegionStart:
286 case SnapToRegionEnd:
287 case SnapToRegionSync:
288 case SnapToRegionBoundary:
289 _editor->build_region_boundary_cache ();
296 /** Call to end a drag `successfully'. Ungrabs item and calls
297 * subclass' finished() method.
299 * @param event GDK event, or 0.
300 * @return true if some movement occurred, otherwise false.
303 Drag::end_grab (GdkEvent* event)
305 _editor->stop_canvas_autoscroll ();
309 finished (event, _move_threshold_passed);
311 _editor->verbose_cursor()->hide ();
313 return _move_threshold_passed;
317 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
321 if (f > _pointer_frame_offset) {
322 pos = f - _pointer_frame_offset;
326 _editor->snap_to_with_modifier (pos, event);
333 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
335 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
339 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
341 /* check to see if we have moved in any way that matters since the last motion event */
342 if (_move_threshold_passed &&
343 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
344 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
348 pair<framecnt_t, int> const threshold = move_threshold ();
350 bool const old_move_threshold_passed = _move_threshold_passed;
352 if (!from_autoscroll && !_move_threshold_passed) {
354 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
355 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
357 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
360 if (active (_editor->mouse_mode) && _move_threshold_passed) {
362 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
363 if (!from_autoscroll) {
364 bool const moving_left = _drags->current_pointer_x() < _last_pointer_x;
365 bool const moving_up = _drags->current_pointer_y() < _last_pointer_y;
366 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), moving_left, moving_up);
369 motion (event, _move_threshold_passed != old_move_threshold_passed);
371 _last_pointer_x = _drags->current_pointer_x ();
372 _last_pointer_y = _drags->current_pointer_y ();
373 _last_pointer_frame = adjusted_current_frame (event);
381 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
389 aborted (_move_threshold_passed);
391 _editor->stop_canvas_autoscroll ();
392 _editor->verbose_cursor()->hide ();
396 Drag::show_verbose_cursor_time (framepos_t frame)
398 _editor->verbose_cursor()->set_time (
400 _drags->current_pointer_x() + 10,
401 _drags->current_pointer_y() + 10
404 _editor->verbose_cursor()->show ();
408 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
410 _editor->verbose_cursor()->show (xoffset);
412 _editor->verbose_cursor()->set_duration (
414 _drags->current_pointer_x() + 10,
415 _drags->current_pointer_y() + 10
420 Drag::show_verbose_cursor_text (string const & text)
422 _editor->verbose_cursor()->show ();
424 _editor->verbose_cursor()->set (
426 _drags->current_pointer_x() + 10,
427 _drags->current_pointer_y() + 10
431 boost::shared_ptr<Region>
432 Drag::add_midi_region (MidiTimeAxisView* view)
434 if (_editor->session()) {
435 const TempoMap& map (_editor->session()->tempo_map());
436 framecnt_t pos = grab_frame();
437 const Meter& m = map.meter_at (pos);
438 /* not that the frame rate used here can be affected by pull up/down which
441 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
442 return view->add_region (grab_frame(), len, true);
445 return boost::shared_ptr<Region>();
448 struct EditorOrderTimeAxisViewSorter {
449 bool operator() (TimeAxisView* a, TimeAxisView* b) {
450 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
451 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
453 return ra->route()->order_key (EditorSort) < rb->route()->order_key (EditorSort);
457 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
461 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
463 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
464 as some of the regions we are dragging may be on such tracks.
467 TrackViewList track_views = _editor->track_views;
468 track_views.sort (EditorOrderTimeAxisViewSorter ());
470 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
471 _time_axis_views.push_back (*i);
473 TimeAxisView::Children children_list = (*i)->get_child_list ();
474 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
475 _time_axis_views.push_back (j->get());
479 /* the list of views can be empty at this point if this is a region list-insert drag
482 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
483 _views.push_back (DraggingView (*i, this));
486 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
490 RegionDrag::region_going_away (RegionView* v)
492 list<DraggingView>::iterator i = _views.begin ();
493 while (i != _views.end() && i->view != v) {
497 if (i != _views.end()) {
502 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
503 * or -1 if it is not found.
506 RegionDrag::find_time_axis_view (TimeAxisView* t) const
509 int const N = _time_axis_views.size ();
510 while (i < N && _time_axis_views[i] != t) {
521 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
522 : RegionDrag (e, i, p, v)
525 , _last_pointer_time_axis_view (0)
526 , _last_pointer_layer (0)
528 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
532 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
534 Drag::start_grab (event, cursor);
536 show_verbose_cursor_time (_last_frame_position);
538 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
540 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
541 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
546 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
548 /* compute the amount of pointer motion in frames, and where
549 the region would be if we moved it by that much.
551 *pending_region_position = adjusted_current_frame (event);
553 framepos_t sync_frame;
554 framecnt_t sync_offset;
557 sync_offset = _primary->region()->sync_offset (sync_dir);
559 /* we don't handle a sync point that lies before zero.
561 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
563 sync_frame = *pending_region_position + (sync_dir*sync_offset);
565 _editor->snap_to_with_modifier (sync_frame, event);
567 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
570 *pending_region_position = _last_frame_position;
573 if (*pending_region_position > max_framepos - _primary->region()->length()) {
574 *pending_region_position = _last_frame_position;
579 /* in locked edit mode, reverse the usual meaning of _x_constrained */
580 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
582 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
584 /* x movement since last time (in pixels) */
585 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
587 /* total x movement */
588 framecnt_t total_dx = *pending_region_position;
589 if (regions_came_from_canvas()) {
590 total_dx = total_dx - grab_frame ();
593 /* check that no regions have gone off the start of the session */
594 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
595 if ((i->view->region()->position() + total_dx) < 0) {
597 *pending_region_position = _last_frame_position;
602 _last_frame_position = *pending_region_position;
609 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
611 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
612 int const n = i->time_axis_view + delta_track;
613 if (n < 0 || n >= int (_time_axis_views.size())) {
614 /* off the top or bottom track */
618 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
619 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
620 /* not a track, or the wrong type */
624 double const l = i->layer + delta_layer;
626 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
627 mode to allow the user to place a region below another on layer 0.
629 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
630 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
631 If it has, the layers will be munged later anyway, so it's ok.
637 /* all regions being dragged are ok with this change */
642 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
644 assert (!_views.empty ());
646 /* Find the TimeAxisView that the pointer is now over */
647 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
649 /* Bail early if we're not over a track */
650 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
652 if (!rtv || !rtv->is_track()) {
653 _editor->verbose_cursor()->hide ();
657 if (first_move && tv.first->view()->layer_display() == Stacked) {
658 tv.first->view()->set_layer_display (Expanded);
661 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
663 /* Here's the current pointer position in terms of time axis view and layer */
664 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
665 double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
667 /* Work out the change in x */
668 framepos_t pending_region_position;
669 double const x_delta = compute_x_delta (event, &pending_region_position);
671 /* Work out the change in y */
673 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
674 double delta_layer = current_pointer_layer - _last_pointer_layer;
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 && 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()) {
703 /* Reparent to a non scrolling group so that we can keep the
704 region selection above all time axis views.
705 Reparenting means that we will have to move the region view
706 within its new parent, as the two parent groups have different coordinates.
709 ArdourCanvas::Group* rvg = rv->get_canvas_group();
710 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
712 rv->get_canvas_group()->reparent (_editor->_region_motion_group);
714 rv->fake_set_opaque (true);
715 rvg->set_position (rv_canvas_offset);
718 /* If we have moved tracks, we'll fudge the layer delta so that the
719 region gets moved back onto layer 0 on its new track; this avoids
720 confusion when dragging regions from non-zero layers onto different
723 double this_delta_layer = delta_layer;
724 if (delta_time_axis_view != 0) {
725 this_delta_layer = - i->layer;
728 /* The TimeAxisView that this region is now on */
729 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
731 /* Ensure it is moved from stacked -> expanded if appropriate */
732 if (tv->view()->layer_display() == Stacked) {
733 tv->view()->set_layer_display (Expanded);
736 /* We're only allowed to go -ve in layer on Expanded views */
737 if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
738 this_delta_layer = - i->layer;
742 rv->set_height (tv->view()->child_height ());
744 /* Update show/hidden status as the region view may have come from a hidden track,
745 or have moved to one.
748 rv->get_canvas_group()->hide ();
750 rv->get_canvas_group()->show ();
753 /* Update the DraggingView */
754 i->time_axis_view += delta_time_axis_view;
755 i->layer += this_delta_layer;
758 _editor->mouse_brush_insert_region (rv, pending_region_position);
763 /* Get the y coordinate of the top of the track that this region is now on */
764 tv->canvas_display()->item_to_canvas (x, y);
766 /* And adjust for the layer that it should be on */
767 StreamView* cv = tv->view ();
768 switch (cv->layer_display ()) {
772 y += (cv->layers() - i->layer - 1) * cv->child_height ();
775 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
779 /* Now move the region view */
780 rv->move (x_delta, y - rv->get_canvas_group()->position().y);
783 } /* foreach region */
785 _total_x_delta += x_delta;
787 if (x_delta != 0 && !_brushing) {
788 show_verbose_cursor_time (_last_frame_position);
791 _last_pointer_time_axis_view += delta_time_axis_view;
792 _last_pointer_layer += delta_layer;
796 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
798 if (_copy && first_move) {
800 /* duplicate the regionview(s) and region(s) */
802 list<DraggingView> new_regionviews;
804 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
806 RegionView* rv = i->view;
807 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
808 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
810 const boost::shared_ptr<const Region> original = rv->region();
811 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
812 region_copy->set_position (original->position());
816 boost::shared_ptr<AudioRegion> audioregion_copy
817 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
819 nrv = new AudioRegionView (*arv, audioregion_copy);
821 boost::shared_ptr<MidiRegion> midiregion_copy
822 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
823 nrv = new MidiRegionView (*mrv, midiregion_copy);
828 nrv->get_canvas_group()->show ();
829 new_regionviews.push_back (DraggingView (nrv, this));
831 /* swap _primary to the copy */
833 if (rv == _primary) {
837 /* ..and deselect the one we copied */
839 rv->set_selected (false);
842 if (!new_regionviews.empty()) {
844 /* reflect the fact that we are dragging the copies */
846 _views = new_regionviews;
848 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
852 RegionMotionDrag::motion (event, first_move);
856 RegionMotionDrag::finished (GdkEvent *, bool)
858 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
863 if ((*i)->view()->layer_display() == Expanded) {
864 (*i)->view()->set_layer_display (Stacked);
870 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
872 RegionMotionDrag::finished (ev, movement_occurred);
874 if (!movement_occurred) {
879 /* reverse this here so that we have the correct logic to finalize
883 if (Config->get_edit_mode() == Lock) {
884 _x_constrained = !_x_constrained;
887 assert (!_views.empty ());
889 /* We might have hidden region views so that they weren't visible during the drag
890 (when they have been reparented). Now everything can be shown again, as region
891 views are back in their track parent groups.
893 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
894 i->view->get_canvas_group()->show ();
897 bool const changed_position = (_last_frame_position != _primary->region()->position());
898 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
899 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
919 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
923 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
925 RegionSelection new_views;
926 PlaylistSet modified_playlists;
927 list<RegionView*> views_to_delete;
930 /* all changes were made during motion event handlers */
932 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
936 _editor->commit_reversible_command ();
940 if (_x_constrained) {
941 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
943 _editor->begin_reversible_command (Operations::region_copy);
946 /* insert the regions into their new playlists */
947 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
949 if (i->view->region()->locked() || i->view->region()->video_locked()) {
955 if (changed_position && !_x_constrained) {
956 where = i->view->region()->position() - drag_delta;
958 where = i->view->region()->position();
961 RegionView* new_view = insert_region_into_playlist (
962 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
969 new_views.push_back (new_view);
971 /* we don't need the copied RegionView any more */
972 views_to_delete.push_back (i->view);
975 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
976 because when views are deleted they are automagically removed from _views, which messes
979 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
983 /* If we've created new regions either by copying or moving
984 to a new track, we want to replace the old selection with the new ones
987 if (new_views.size() > 0) {
988 _editor->selection->set (new_views);
991 /* write commands for the accumulated diffs for all our modified playlists */
992 add_stateful_diff_commands_for_playlists (modified_playlists);
994 _editor->commit_reversible_command ();
998 RegionMoveDrag::finished_no_copy (
999 bool const changed_position,
1000 bool const changed_tracks,
1001 framecnt_t const drag_delta
1004 RegionSelection new_views;
1005 PlaylistSet modified_playlists;
1006 PlaylistSet frozen_playlists;
1007 set<RouteTimeAxisView*> views_to_update;
1010 /* all changes were made during motion event handlers */
1011 _editor->commit_reversible_command ();
1015 if (_x_constrained) {
1016 _editor->begin_reversible_command (_("fixed time region drag"));
1018 _editor->begin_reversible_command (Operations::region_drag);
1021 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1023 RegionView* rv = i->view;
1025 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1026 double const dest_layer = i->layer;
1028 if (rv->region()->locked() || rv->region()->video_locked()) {
1033 views_to_update.insert (dest_rtv);
1037 if (changed_position && !_x_constrained) {
1038 where = rv->region()->position() - drag_delta;
1040 where = rv->region()->position();
1043 if (changed_tracks) {
1045 /* insert into new playlist */
1047 RegionView* new_view = insert_region_into_playlist (
1048 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1051 if (new_view == 0) {
1056 new_views.push_back (new_view);
1058 /* remove from old playlist */
1060 /* the region that used to be in the old playlist is not
1061 moved to the new one - we use a copy of it. as a result,
1062 any existing editor for the region should no longer be
1065 rv->hide_region_editor();
1066 rv->fake_set_opaque (false);
1068 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1072 rv->region()->clear_changes ();
1075 motion on the same track. plonk the previously reparented region
1076 back to its original canvas group (its streamview).
1077 No need to do anything for copies as they are fake regions which will be deleted.
1080 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1081 rv->get_canvas_group()->set_y_position (i->initial_y);
1084 /* just change the model */
1086 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1088 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1089 playlist->set_layer (rv->region(), dest_layer);
1092 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1094 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1097 playlist->freeze ();
1100 /* this movement may result in a crossfade being modified, so we need to get undo
1101 data from the playlist as well as the region.
1104 r = modified_playlists.insert (playlist);
1106 playlist->clear_changes ();
1109 rv->region()->set_position (where);
1111 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1114 if (changed_tracks) {
1116 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1117 was selected in all of them, then removing it from a playlist will have removed all
1118 trace of it from _views (i.e. there were N regions selected, we removed 1,
1119 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1120 corresponding regionview, and _views is now empty).
1122 This could have invalidated any and all iterators into _views.
1124 The heuristic we use here is: if the region selection is empty, break out of the loop
1125 here. if the region selection is not empty, then restart the loop because we know that
1126 we must have removed at least the region(view) we've just been working on as well as any
1127 that we processed on previous iterations.
1129 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1130 we can just iterate.
1134 if (_views.empty()) {
1145 /* If we've created new regions either by copying or moving
1146 to a new track, we want to replace the old selection with the new ones
1149 if (new_views.size() > 0) {
1150 _editor->selection->set (new_views);
1153 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1157 /* write commands for the accumulated diffs for all our modified playlists */
1158 add_stateful_diff_commands_for_playlists (modified_playlists);
1160 _editor->commit_reversible_command ();
1162 /* We have futzed with the layering of canvas items on our streamviews.
1163 If any region changed layer, this will have resulted in the stream
1164 views being asked to set up their region views, and all will be well.
1165 If not, we might now have badly-ordered region views. Ask the StreamViews
1166 involved to sort themselves out, just in case.
1169 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1170 (*i)->view()->playlist_layered ((*i)->track ());
1174 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1175 * @param region Region to remove.
1176 * @param playlist playlist To remove from.
1177 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1178 * that clear_changes () is only called once per playlist.
1181 RegionMoveDrag::remove_region_from_playlist (
1182 boost::shared_ptr<Region> region,
1183 boost::shared_ptr<Playlist> playlist,
1184 PlaylistSet& modified_playlists
1187 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1190 playlist->clear_changes ();
1193 playlist->remove_region (region);
1197 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1198 * clearing the playlist's diff history first if necessary.
1199 * @param region Region to insert.
1200 * @param dest_rtv Destination RouteTimeAxisView.
1201 * @param dest_layer Destination layer.
1202 * @param where Destination position.
1203 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1204 * that clear_changes () is only called once per playlist.
1205 * @return New RegionView, or 0 if no insert was performed.
1208 RegionMoveDrag::insert_region_into_playlist (
1209 boost::shared_ptr<Region> region,
1210 RouteTimeAxisView* dest_rtv,
1213 PlaylistSet& modified_playlists
1216 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1217 if (!dest_playlist) {
1221 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1222 _new_region_view = 0;
1223 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1225 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1226 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1228 dest_playlist->clear_changes ();
1231 dest_playlist->add_region (region, where);
1233 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1234 dest_playlist->set_layer (region, dest_layer);
1239 assert (_new_region_view);
1241 return _new_region_view;
1245 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1247 _new_region_view = rv;
1251 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1253 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1254 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1256 _editor->session()->add_command (c);
1265 RegionMoveDrag::aborted (bool movement_occurred)
1269 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1276 RegionMotionDrag::aborted (movement_occurred);
1281 RegionMotionDrag::aborted (bool)
1283 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1284 if ((*i)->view()->layer_display() == Expanded) {
1285 (*i)->view()->set_layer_display (Stacked);
1289 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1290 RegionView* rv = i->view;
1291 TimeAxisView* tv = &(rv->get_time_axis_view ());
1292 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1294 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1295 rv->get_canvas_group()->set_y_position (0);
1297 rv->fake_set_opaque (false);
1298 rv->move (-_total_x_delta, 0);
1299 rv->set_height (rtv->view()->child_height ());
1303 /** @param b true to brush, otherwise false.
1304 * @param c true to make copies of the regions being moved, otherwise false.
1306 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1307 : RegionMotionDrag (e, i, p, v, b),
1310 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1313 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1314 if (rtv && rtv->is_track()) {
1315 speed = rtv->track()->speed ();
1318 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1322 RegionMoveDrag::setup_pointer_frame_offset ()
1324 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1327 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1328 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1330 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1332 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1333 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1335 _primary = v->view()->create_region_view (r, false, false);
1337 _primary->get_canvas_group()->show ();
1338 _primary->set_position (pos, 0);
1339 _views.push_back (DraggingView (_primary, this));
1341 _last_frame_position = pos;
1343 _item = _primary->get_canvas_group ();
1347 RegionInsertDrag::finished (GdkEvent *, bool)
1349 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1351 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1352 _primary->get_canvas_group()->set_y_position (0);
1354 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1356 _editor->begin_reversible_command (Operations::insert_region);
1357 playlist->clear_changes ();
1358 playlist->add_region (_primary->region (), _last_frame_position);
1359 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1360 _editor->commit_reversible_command ();
1368 RegionInsertDrag::aborted (bool)
1375 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1376 : RegionMoveDrag (e, i, p, v, false, false)
1378 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1381 struct RegionSelectionByPosition {
1382 bool operator() (RegionView*a, RegionView* b) {
1383 return a->region()->position () < b->region()->position();
1388 RegionSpliceDrag::motion (GdkEvent* event, bool)
1390 /* Which trackview is this ? */
1392 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1393 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1395 /* The region motion is only processed if the pointer is over
1399 if (!tv || !tv->is_track()) {
1400 /* To make sure we hide the verbose canvas cursor when the mouse is
1401 not held over and audiotrack.
1403 _editor->verbose_cursor()->hide ();
1409 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1415 RegionSelection copy (_editor->selection->regions);
1417 RegionSelectionByPosition cmp;
1420 framepos_t const pf = adjusted_current_frame (event);
1422 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1424 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1430 boost::shared_ptr<Playlist> playlist;
1432 if ((playlist = atv->playlist()) == 0) {
1436 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1441 if (pf < (*i)->region()->last_frame() + 1) {
1445 if (pf > (*i)->region()->first_frame()) {
1451 playlist->shuffle ((*i)->region(), dir);
1456 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1458 RegionMoveDrag::finished (event, movement_occurred);
1462 RegionSpliceDrag::aborted (bool)
1467 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1469 _view (dynamic_cast<MidiTimeAxisView*> (v))
1471 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1477 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1480 _region = add_midi_region (_view);
1481 _view->playlist()->freeze ();
1484 framepos_t const f = adjusted_current_frame (event);
1485 if (f < grab_frame()) {
1486 _region->set_position (f);
1489 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1490 so that if this region is duplicated, its duplicate starts on
1491 a snap point rather than 1 frame after a snap point. Otherwise things get
1492 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1493 place snapped notes at the start of the region.
1496 framecnt_t const len = (framecnt_t) fabs (f - grab_frame () - 1);
1497 _region->set_length (len < 1 ? 1 : len);
1503 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1505 if (!movement_occurred) {
1506 add_midi_region (_view);
1508 _view->playlist()->thaw ();
1513 RegionCreateDrag::aborted (bool)
1516 _view->playlist()->thaw ();
1522 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1526 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1530 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1532 Gdk::Cursor* cursor;
1533 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1535 float x_fraction = cnote->mouse_x_fraction ();
1537 if (x_fraction > 0.0 && x_fraction < 0.25) {
1538 cursor = _editor->cursors()->left_side_trim;
1540 cursor = _editor->cursors()->right_side_trim;
1543 Drag::start_grab (event, cursor);
1545 region = &cnote->region_view();
1547 double const region_start = region->get_position_pixels();
1548 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1550 if (grab_x() <= middle_point) {
1551 cursor = _editor->cursors()->left_side_trim;
1554 cursor = _editor->cursors()->right_side_trim;
1560 if (event->motion.state & Keyboard::PrimaryModifier) {
1566 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1568 if (ms.size() > 1) {
1569 /* has to be relative, may make no sense otherwise */
1573 /* select this note; if it is already selected, preserve the existing selection,
1574 otherwise make this note the only one selected.
1576 region->note_selected (cnote, cnote->selected ());
1578 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1579 MidiRegionSelection::iterator next;
1582 (*r)->begin_resizing (at_front);
1588 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1590 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1591 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1592 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1594 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1599 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1601 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1602 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1603 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1605 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1610 NoteResizeDrag::aborted (bool)
1612 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1613 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1614 (*r)->abort_resizing ();
1618 AVDraggingView::AVDraggingView (RegionView* v)
1621 initial_position = v->region()->position ();
1624 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1627 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1630 TrackViewList empty;
1632 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1633 std::list<RegionView*> views = rs.by_layer();
1635 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1636 RegionView* rv = (*i);
1637 if (!rv->region()->video_locked()) {
1640 _views.push_back (AVDraggingView (rv));
1645 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1647 Drag::start_grab (event);
1648 if (_editor->session() == 0) {
1652 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1653 _max_backwards_drag = (
1654 ARDOUR_UI::instance()->video_timeline->get_duration()
1655 + ARDOUR_UI::instance()->video_timeline->get_offset()
1656 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1659 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1660 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1661 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1664 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1667 Timecode::Time timecode;
1668 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1669 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);
1670 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1671 _editor->verbose_cursor()->show ();
1675 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1677 if (_editor->session() == 0) {
1680 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1684 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1685 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(dt);
1687 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1688 dt = - _max_backwards_drag;
1691 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1692 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1694 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1695 RegionView* rv = i->view;
1696 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1699 rv->fake_set_opaque (true);
1700 rv->region()->clear_changes ();
1701 rv->region()->suspend_property_changes();
1703 rv->region()->set_position(i->initial_position + dt);
1704 rv->region_changed(ARDOUR::Properties::position);
1707 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1708 Timecode::Time timecode;
1709 Timecode::Time timediff;
1711 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1712 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1713 snprintf (buf, sizeof (buf),
1714 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1715 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1716 , _("Video Start:"),
1717 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1719 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1721 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1722 _editor->verbose_cursor()->show ();
1726 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1728 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1732 if (!movement_occurred || ! _editor->session()) {
1736 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1738 _editor->begin_reversible_command (_("Move Video"));
1740 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1741 ARDOUR_UI::instance()->video_timeline->save_undo();
1742 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1743 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1745 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1746 i->view->drag_end();
1747 i->view->fake_set_opaque (false);
1748 i->view->region()->resume_property_changes ();
1750 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1753 _editor->session()->maybe_update_session_range(
1754 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1755 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1759 _editor->commit_reversible_command ();
1763 VideoTimeLineDrag::aborted (bool)
1765 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1768 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1769 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1771 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1772 i->view->region()->resume_property_changes ();
1773 i->view->region()->set_position(i->initial_position);
1777 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1778 : RegionDrag (e, i, p, v)
1780 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1781 _preserve_fade_anchor = preserve_fade_anchor;
1785 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1788 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1789 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1791 if (tv && tv->is_track()) {
1792 speed = tv->track()->speed();
1795 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1796 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1797 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1799 framepos_t const pf = adjusted_current_frame (event);
1801 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1802 /* Move the contents of the region around without changing the region bounds */
1803 _operation = ContentsTrim;
1804 Drag::start_grab (event, _editor->cursors()->trimmer);
1806 /* These will get overridden for a point trim.*/
1807 if (pf < (region_start + region_length/2)) {
1808 /* closer to front */
1809 _operation = StartTrim;
1810 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1813 _operation = EndTrim;
1814 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1818 switch (_operation) {
1820 show_verbose_cursor_time (region_start);
1821 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1822 i->view->trim_front_starting ();
1826 show_verbose_cursor_time (region_end);
1829 show_verbose_cursor_time (pf);
1833 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1834 i->view->region()->suspend_property_changes ();
1839 TrimDrag::motion (GdkEvent* event, bool first_move)
1841 RegionView* rv = _primary;
1844 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1845 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1846 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1847 frameoffset_t frame_delta = 0;
1849 if (tv && tv->is_track()) {
1850 speed = tv->track()->speed();
1853 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1859 switch (_operation) {
1861 trim_type = "Region start trim";
1864 trim_type = "Region end trim";
1867 trim_type = "Region content trim";
1871 _editor->begin_reversible_command (trim_type);
1873 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1874 RegionView* rv = i->view;
1875 rv->fake_set_opaque (false);
1876 rv->enable_display (false);
1877 rv->region()->playlist()->clear_owned_changes ();
1879 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1882 arv->temporarily_hide_envelope ();
1886 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1887 insert_result = _editor->motion_frozen_playlists.insert (pl);
1889 if (insert_result.second) {
1895 bool non_overlap_trim = false;
1897 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1898 non_overlap_trim = true;
1901 switch (_operation) {
1903 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1904 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1905 if (changed && _preserve_fade_anchor) {
1906 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1911 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1912 distance = _drags->current_pointer_x() - grab_x();
1913 len = ar->fade_in()->back()->when;
1914 new_length = len - _editor->pixel_to_sample (distance);
1915 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
1916 arv->reset_fade_in_shape_width (ar, new_length); //the grey shape
1923 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1924 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1925 if (changed && _preserve_fade_anchor) {
1926 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1931 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1932 distance = grab_x() - _drags->current_pointer_x();
1933 len = ar->fade_out()->back()->when;
1934 new_length = len - _editor->pixel_to_sample (distance);
1935 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
1936 arv->reset_fade_out_shape_width (ar, new_length); //the grey shape
1944 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1945 // frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1947 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1948 i->view->move_contents (frame_delta);
1954 switch (_operation) {
1956 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1959 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1962 // show_verbose_cursor_time (frame_delta);
1969 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1971 if (movement_occurred) {
1972 motion (event, false);
1974 if (_operation == StartTrim) {
1975 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1977 /* This must happen before the region's StatefulDiffCommand is created, as it may
1978 `correct' (ahem) the region's _start from being negative to being zero. It
1979 needs to be zero in the undo record.
1981 i->view->trim_front_ending ();
1983 if (_preserve_fade_anchor) {
1984 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1989 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1990 distance = _drags->current_pointer_x() - grab_x();
1991 len = ar->fade_in()->back()->when;
1992 new_length = len - _editor->pixel_to_sample (distance);
1993 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
1994 ar->set_fade_in_length(new_length);
1998 } else if (_operation == EndTrim) {
1999 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2000 if (_preserve_fade_anchor) {
2001 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2006 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2007 distance = _drags->current_pointer_x() - grab_x();
2008 len = ar->fade_out()->back()->when;
2009 new_length = len - _editor->pixel_to_sample (distance);
2010 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2011 ar->set_fade_out_length(new_length);
2017 if (!_views.empty()) {
2018 if (_operation == StartTrim) {
2019 _editor->maybe_locate_with_edit_preroll(
2020 _views.begin()->view->region()->position());
2022 if (_operation == EndTrim) {
2023 _editor->maybe_locate_with_edit_preroll(
2024 _views.begin()->view->region()->position() +
2025 _views.begin()->view->region()->length());
2029 if (!_editor->selection->selected (_primary)) {
2030 _primary->thaw_after_trim ();
2033 set<boost::shared_ptr<Playlist> > diffed_playlists;
2035 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2036 i->view->thaw_after_trim ();
2037 i->view->enable_display (true);
2038 i->view->fake_set_opaque (true);
2040 /* Trimming one region may affect others on the playlist, so we need
2041 to get undo Commands from the whole playlist rather than just the
2042 region. Use diffed_playlists to make sure we don't diff a given
2043 playlist more than once.
2045 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2046 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2047 vector<Command*> cmds;
2049 _editor->session()->add_commands (cmds);
2050 diffed_playlists.insert (p);
2055 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2059 _editor->motion_frozen_playlists.clear ();
2060 _editor->commit_reversible_command();
2063 /* no mouse movement */
2064 _editor->point_trim (event, adjusted_current_frame (event));
2067 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2068 if (_operation == StartTrim) {
2069 i->view->trim_front_ending ();
2072 i->view->region()->resume_property_changes ();
2077 TrimDrag::aborted (bool movement_occurred)
2079 /* Our motion method is changing model state, so use the Undo system
2080 to cancel. Perhaps not ideal, as this will leave an Undo point
2081 behind which may be slightly odd from the user's point of view.
2086 if (movement_occurred) {
2090 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2091 i->view->region()->resume_property_changes ();
2096 TrimDrag::setup_pointer_frame_offset ()
2098 list<DraggingView>::iterator i = _views.begin ();
2099 while (i != _views.end() && i->view != _primary) {
2103 if (i == _views.end()) {
2107 switch (_operation) {
2109 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2112 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2119 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2123 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2124 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2129 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2131 Drag::start_grab (event, cursor);
2132 show_verbose_cursor_time (adjusted_current_frame(event));
2136 MeterMarkerDrag::setup_pointer_frame_offset ()
2138 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2142 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2146 // create a dummy marker for visual representation of moving the
2147 // section, because whether its a copy or not, we're going to
2148 // leave or lose the original marker (leave if its a copy; lose if its
2149 // not, because we'll remove it from the map).
2151 MeterSection section (_marker->meter());
2153 if (!section.movable()) {
2158 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2160 _marker = new MeterMarker (
2162 *_editor->meter_group,
2163 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2165 *new MeterSection (_marker->meter())
2168 /* use the new marker for the grab */
2169 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2172 TempoMap& map (_editor->session()->tempo_map());
2173 /* get current state */
2174 before_state = &map.get_state();
2175 /* remove the section while we drag it */
2176 map.remove_meter (section, true);
2180 framepos_t const pf = adjusted_current_frame (event);
2181 _marker->set_position (pf);
2182 show_verbose_cursor_time (pf);
2186 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2188 if (!movement_occurred) {
2192 motion (event, false);
2194 Timecode::BBT_Time when;
2196 TempoMap& map (_editor->session()->tempo_map());
2197 map.bbt_time (last_pointer_frame(), when);
2199 if (_copy == true) {
2200 _editor->begin_reversible_command (_("copy meter mark"));
2201 XMLNode &before = map.get_state();
2202 map.add_meter (_marker->meter(), when);
2203 XMLNode &after = map.get_state();
2204 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2205 _editor->commit_reversible_command ();
2208 _editor->begin_reversible_command (_("move meter mark"));
2210 /* we removed it before, so add it back now */
2212 map.add_meter (_marker->meter(), when);
2213 XMLNode &after = map.get_state();
2214 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2215 _editor->commit_reversible_command ();
2218 // delete the dummy marker we used for visual representation while moving.
2219 // a new visual marker will show up automatically.
2224 MeterMarkerDrag::aborted (bool moved)
2226 _marker->set_position (_marker->meter().frame ());
2229 TempoMap& map (_editor->session()->tempo_map());
2230 /* we removed it before, so add it back now */
2231 map.add_meter (_marker->meter(), _marker->meter().frame());
2232 // delete the dummy marker we used for visual representation while moving.
2233 // a new visual marker will show up automatically.
2238 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2242 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2244 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2249 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2251 Drag::start_grab (event, cursor);
2252 show_verbose_cursor_time (adjusted_current_frame (event));
2256 TempoMarkerDrag::setup_pointer_frame_offset ()
2258 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2262 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2266 // create a dummy marker for visual representation of moving the
2267 // section, because whether its a copy or not, we're going to
2268 // leave or lose the original marker (leave if its a copy; lose if its
2269 // not, because we'll remove it from the map).
2271 // create a dummy marker for visual representation of moving the copy.
2272 // The actual copying is not done before we reach the finish callback.
2275 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2277 TempoSection section (_marker->tempo());
2279 _marker = new TempoMarker (
2281 *_editor->tempo_group,
2282 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2284 *new TempoSection (_marker->tempo())
2287 /* use the new marker for the grab */
2288 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2291 TempoMap& map (_editor->session()->tempo_map());
2292 /* get current state */
2293 before_state = &map.get_state();
2294 /* remove the section while we drag it */
2295 map.remove_tempo (section, true);
2299 framepos_t const pf = adjusted_current_frame (event);
2300 _marker->set_position (pf);
2301 show_verbose_cursor_time (pf);
2305 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2307 if (!movement_occurred) {
2311 motion (event, false);
2313 TempoMap& map (_editor->session()->tempo_map());
2314 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2315 Timecode::BBT_Time when;
2317 map.bbt_time (beat_time, when);
2319 if (_copy == true) {
2320 _editor->begin_reversible_command (_("copy tempo mark"));
2321 XMLNode &before = map.get_state();
2322 map.add_tempo (_marker->tempo(), when);
2323 XMLNode &after = map.get_state();
2324 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2325 _editor->commit_reversible_command ();
2328 _editor->begin_reversible_command (_("move tempo mark"));
2329 /* we removed it before, so add it back now */
2330 map.add_tempo (_marker->tempo(), when);
2331 XMLNode &after = map.get_state();
2332 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2333 _editor->commit_reversible_command ();
2336 // delete the dummy marker we used for visual representation while moving.
2337 // a new visual marker will show up automatically.
2342 TempoMarkerDrag::aborted (bool moved)
2344 _marker->set_position (_marker->tempo().frame());
2346 TempoMap& map (_editor->session()->tempo_map());
2347 /* we removed it before, so add it back now */
2348 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2349 // delete the dummy marker we used for visual representation while moving.
2350 // a new visual marker will show up automatically.
2355 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2356 : Drag (e, &c.time_bar_canvas_item())
2360 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2363 /** Do all the things we do when dragging the playhead to make it look as though
2364 * we have located, without actually doing the locate (because that would cause
2365 * the diskstream buffers to be refilled, which is too slow).
2368 CursorDrag::fake_locate (framepos_t t)
2370 _editor->playhead_cursor->set_position (t);
2372 Session* s = _editor->session ();
2373 if (s->timecode_transmission_suspended ()) {
2374 framepos_t const f = _editor->playhead_cursor->current_frame ();
2375 s->send_mmc_locate (f);
2376 s->send_full_time_code (f);
2379 show_verbose_cursor_time (t);
2380 _editor->UpdateAllTransportClocks (t);
2384 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2386 Drag::start_grab (event, c);
2388 _grab_zoom = _editor->samples_per_pixel;
2390 framepos_t where = _editor->canvas_event_frame (event, 0, 0);
2392 _editor->snap_to_with_modifier (where, event);
2394 _editor->_dragging_playhead = true;
2396 Session* s = _editor->session ();
2398 /* grab the track canvas item as well */
2400 _cursor.track_canvas_item().grab();
2403 if (_was_rolling && _stop) {
2407 if (s->is_auditioning()) {
2408 s->cancel_audition ();
2412 if (AudioEngine::instance()->connected()) {
2414 /* do this only if we're the engine is connected
2415 * because otherwise this request will never be
2416 * serviced and we'll busy wait forever. likewise,
2417 * notice if we are disconnected while waiting for the
2418 * request to be serviced.
2421 s->request_suspend_timecode_transmission ();
2422 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2423 /* twiddle our thumbs */
2428 fake_locate (where);
2432 CursorDrag::motion (GdkEvent* event, bool)
2434 framepos_t const adjusted_frame = adjusted_current_frame (event);
2435 if (adjusted_frame != last_pointer_frame()) {
2436 fake_locate (adjusted_frame);
2441 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2443 _editor->_dragging_playhead = false;
2445 _cursor.track_canvas_item().ungrab();
2447 if (!movement_occurred && _stop) {
2451 motion (event, false);
2453 Session* s = _editor->session ();
2455 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2456 _editor->_pending_locate_request = true;
2457 s->request_resume_timecode_transmission ();
2462 CursorDrag::aborted (bool)
2464 _cursor.track_canvas_item().ungrab();
2466 if (_editor->_dragging_playhead) {
2467 _editor->session()->request_resume_timecode_transmission ();
2468 _editor->_dragging_playhead = false;
2471 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2474 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2475 : RegionDrag (e, i, p, v)
2477 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2481 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2483 Drag::start_grab (event, cursor);
2485 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2486 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2488 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2492 FadeInDrag::setup_pointer_frame_offset ()
2494 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2495 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2496 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2500 FadeInDrag::motion (GdkEvent* event, bool)
2502 framecnt_t fade_length;
2504 framepos_t const pos = adjusted_current_frame (event);
2506 boost::shared_ptr<Region> region = _primary->region ();
2508 if (pos < (region->position() + 64)) {
2509 fade_length = 64; // this should be a minimum defined somewhere
2510 } else if (pos > region->last_frame()) {
2511 fade_length = region->length();
2513 fade_length = pos - region->position();
2516 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2518 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2524 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2527 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2531 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2533 if (!movement_occurred) {
2537 framecnt_t fade_length;
2539 framepos_t const pos = adjusted_current_frame (event);
2541 boost::shared_ptr<Region> region = _primary->region ();
2543 if (pos < (region->position() + 64)) {
2544 fade_length = 64; // this should be a minimum defined somewhere
2545 } else if (pos > region->last_frame()) {
2546 fade_length = region->length();
2548 fade_length = pos - region->position();
2551 _editor->begin_reversible_command (_("change fade in length"));
2553 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2555 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2561 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2562 XMLNode &before = alist->get_state();
2564 tmp->audio_region()->set_fade_in_length (fade_length);
2565 tmp->audio_region()->set_fade_in_active (true);
2567 XMLNode &after = alist->get_state();
2568 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2571 _editor->commit_reversible_command ();
2575 FadeInDrag::aborted (bool)
2577 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2578 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2584 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2588 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2589 : RegionDrag (e, i, p, v)
2591 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2595 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2597 Drag::start_grab (event, cursor);
2599 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2600 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2602 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2606 FadeOutDrag::setup_pointer_frame_offset ()
2608 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2609 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2610 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2614 FadeOutDrag::motion (GdkEvent* event, bool)
2616 framecnt_t fade_length;
2618 framepos_t const pos = adjusted_current_frame (event);
2620 boost::shared_ptr<Region> region = _primary->region ();
2622 if (pos > (region->last_frame() - 64)) {
2623 fade_length = 64; // this should really be a minimum fade defined somewhere
2625 else if (pos < region->position()) {
2626 fade_length = region->length();
2629 fade_length = region->last_frame() - pos;
2632 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2634 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2640 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2643 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2647 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2649 if (!movement_occurred) {
2653 framecnt_t fade_length;
2655 framepos_t const pos = adjusted_current_frame (event);
2657 boost::shared_ptr<Region> region = _primary->region ();
2659 if (pos > (region->last_frame() - 64)) {
2660 fade_length = 64; // this should really be a minimum fade defined somewhere
2662 else if (pos < region->position()) {
2663 fade_length = region->length();
2666 fade_length = region->last_frame() - pos;
2669 _editor->begin_reversible_command (_("change fade out length"));
2671 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2673 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2679 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2680 XMLNode &before = alist->get_state();
2682 tmp->audio_region()->set_fade_out_length (fade_length);
2683 tmp->audio_region()->set_fade_out_active (true);
2685 XMLNode &after = alist->get_state();
2686 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2689 _editor->commit_reversible_command ();
2693 FadeOutDrag::aborted (bool)
2695 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2696 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2702 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2706 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2709 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2711 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2714 _points.push_back (ArdourCanvas::Duple (0, 0));
2715 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2718 MarkerDrag::~MarkerDrag ()
2720 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2725 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2727 location = new Location (*l);
2728 markers.push_back (m);
2733 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2735 Drag::start_grab (event, cursor);
2739 Location *location = _editor->find_location_from_marker (_marker, is_start);
2740 _editor->_dragging_edit_point = true;
2742 update_item (location);
2744 // _drag_line->show();
2745 // _line->raise_to_top();
2748 show_verbose_cursor_time (location->start());
2750 show_verbose_cursor_time (location->end());
2753 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2756 case Selection::Toggle:
2757 /* we toggle on the button release */
2759 case Selection::Set:
2760 if (!_editor->selection->selected (_marker)) {
2761 _editor->selection->set (_marker);
2764 case Selection::Extend:
2766 Locations::LocationList ll;
2767 list<Marker*> to_add;
2769 _editor->selection->markers.range (s, e);
2770 s = min (_marker->position(), s);
2771 e = max (_marker->position(), e);
2774 if (e < max_framepos) {
2777 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2778 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2779 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2782 to_add.push_back (lm->start);
2785 to_add.push_back (lm->end);
2789 if (!to_add.empty()) {
2790 _editor->selection->add (to_add);
2794 case Selection::Add:
2795 _editor->selection->add (_marker);
2799 /* Set up copies for us to manipulate during the drag
2802 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2804 Location* l = _editor->find_location_from_marker (*i, is_start);
2811 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2813 /* range: check that the other end of the range isn't
2816 CopiedLocationInfo::iterator x;
2817 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2818 if (*(*x).location == *l) {
2822 if (x == _copied_locations.end()) {
2823 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2825 (*x).markers.push_back (*i);
2826 (*x).move_both = true;
2834 MarkerDrag::setup_pointer_frame_offset ()
2837 Location *location = _editor->find_location_from_marker (_marker, is_start);
2838 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2842 MarkerDrag::motion (GdkEvent* event, bool)
2844 framecnt_t f_delta = 0;
2846 bool move_both = false;
2847 Location *real_location;
2848 Location *copy_location = 0;
2850 framepos_t const newframe = adjusted_current_frame (event);
2851 framepos_t next = newframe;
2853 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2857 CopiedLocationInfo::iterator x;
2859 /* find the marker we're dragging, and compute the delta */
2861 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2863 copy_location = (*x).location;
2865 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2867 /* this marker is represented by this
2868 * CopiedLocationMarkerInfo
2871 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
2876 if (real_location->is_mark()) {
2877 f_delta = newframe - copy_location->start();
2881 switch (_marker->type()) {
2882 case Marker::SessionStart:
2883 case Marker::RangeStart:
2884 case Marker::LoopStart:
2885 case Marker::PunchIn:
2886 f_delta = newframe - copy_location->start();
2889 case Marker::SessionEnd:
2890 case Marker::RangeEnd:
2891 case Marker::LoopEnd:
2892 case Marker::PunchOut:
2893 f_delta = newframe - copy_location->end();
2896 /* what kind of marker is this ? */
2905 if (x == _copied_locations.end()) {
2906 /* hmm, impossible - we didn't find the dragged marker */
2910 /* now move them all */
2912 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2914 copy_location = x->location;
2916 /* call this to find out if its the start or end */
2918 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
2922 if (real_location->locked()) {
2926 if (copy_location->is_mark()) {
2930 copy_location->set_start (copy_location->start() + f_delta);
2934 framepos_t new_start = copy_location->start() + f_delta;
2935 framepos_t new_end = copy_location->end() + f_delta;
2937 if (is_start) { // start-of-range marker
2939 if (move_both || (*x).move_both) {
2940 copy_location->set_start (new_start);
2941 copy_location->set_end (new_end);
2942 } else if (new_start < copy_location->end()) {
2943 copy_location->set_start (new_start);
2944 } else if (newframe > 0) {
2945 _editor->snap_to (next, 1, true);
2946 copy_location->set_end (next);
2947 copy_location->set_start (newframe);
2950 } else { // end marker
2952 if (move_both || (*x).move_both) {
2953 copy_location->set_end (new_end);
2954 copy_location->set_start (new_start);
2955 } else if (new_end > copy_location->start()) {
2956 copy_location->set_end (new_end);
2957 } else if (newframe > 0) {
2958 _editor->snap_to (next, -1, true);
2959 copy_location->set_start (next);
2960 copy_location->set_end (newframe);
2965 update_item (copy_location);
2967 /* now lookup the actual GUI items used to display this
2968 * location and move them to wherever the copy of the location
2969 * is now. This means that the logic in ARDOUR::Location is
2970 * still enforced, even though we are not (yet) modifying
2971 * the real Location itself.
2974 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2977 lm->set_position (copy_location->start(), copy_location->end());
2982 assert (!_copied_locations.empty());
2984 show_verbose_cursor_time (newframe);
2988 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2990 if (!movement_occurred) {
2992 if (was_double_click()) {
2993 cerr << "End of marker double click\n";
2996 /* just a click, do nothing but finish
2997 off the selection process
3000 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3003 case Selection::Set:
3004 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3005 _editor->selection->set (_marker);
3009 case Selection::Toggle:
3010 /* we toggle on the button release, click only */
3011 _editor->selection->toggle (_marker);
3014 case Selection::Extend:
3015 case Selection::Add:
3022 _editor->_dragging_edit_point = false;
3024 _editor->begin_reversible_command ( _("move marker") );
3025 XMLNode &before = _editor->session()->locations()->get_state();
3027 MarkerSelection::iterator i;
3028 CopiedLocationInfo::iterator x;
3031 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3032 x != _copied_locations.end() && i != _editor->selection->markers.end();
3035 Location * location = _editor->find_location_from_marker (*i, is_start);
3039 if (location->locked()) {
3043 if (location->is_mark()) {
3044 location->set_start (((*x).location)->start());
3046 location->set (((*x).location)->start(), ((*x).location)->end());
3051 XMLNode &after = _editor->session()->locations()->get_state();
3052 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3053 _editor->commit_reversible_command ();
3057 MarkerDrag::aborted (bool)
3063 MarkerDrag::update_item (Location*)
3068 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3070 _cumulative_x_drag (0),
3071 _cumulative_y_drag (0)
3073 if (_zero_gain_fraction < 0.0) {
3074 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3077 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3079 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3085 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3087 Drag::start_grab (event, _editor->cursors()->fader);
3089 // start the grab at the center of the control point so
3090 // the point doesn't 'jump' to the mouse after the first drag
3091 _fixed_grab_x = _point->get_x();
3092 _fixed_grab_y = _point->get_y();
3094 float const fraction = 1 - (_point->get_y() / _point->line().height());
3096 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3098 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3099 event->button.x + 10, event->button.y + 10);
3101 _editor->verbose_cursor()->show ();
3103 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3105 if (!_point->can_slide ()) {
3106 _x_constrained = true;
3111 ControlPointDrag::motion (GdkEvent* event, bool)
3113 double dx = _drags->current_pointer_x() - last_pointer_x();
3114 double dy = _drags->current_pointer_y() - last_pointer_y();
3116 if (event->button.state & Keyboard::SecondaryModifier) {
3121 /* coordinate in pixels relative to the start of the region (for region-based automation)
3122 or track (for track-based automation) */
3123 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3124 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3126 // calculate zero crossing point. back off by .01 to stay on the
3127 // positive side of zero
3128 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3130 // make sure we hit zero when passing through
3131 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3135 if (_x_constrained) {
3138 if (_y_constrained) {
3142 _cumulative_x_drag = cx - _fixed_grab_x;
3143 _cumulative_y_drag = cy - _fixed_grab_y;
3147 cy = min ((double) _point->line().height(), cy);
3149 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3151 if (!_x_constrained) {
3152 _editor->snap_to_with_modifier (cx_frames, event);
3155 cx_frames = min (cx_frames, _point->line().maximum_time());
3157 float const fraction = 1.0 - (cy / _point->line().height());
3159 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3161 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3165 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3167 if (!movement_occurred) {
3171 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3172 _editor->reset_point_selection ();
3176 motion (event, false);
3179 _point->line().end_drag (_pushing, _final_index);
3180 _editor->session()->commit_reversible_command ();
3184 ControlPointDrag::aborted (bool)
3186 _point->line().reset ();
3190 ControlPointDrag::active (Editing::MouseMode m)
3192 if (m == Editing::MouseGain) {
3193 /* always active in mouse gain */
3197 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3198 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3201 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3204 _cumulative_y_drag (0)
3206 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3210 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3212 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3215 _item = &_line->grab_item ();
3217 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3218 origin, and ditto for y.
3221 double cx = event->button.x;
3222 double cy = event->button.y;
3224 _line->parent_group().canvas_to_item (cx, cy);
3226 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3231 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3232 /* no adjacent points */
3236 Drag::start_grab (event, _editor->cursors()->fader);
3238 /* store grab start in parent frame */
3243 double fraction = 1.0 - (cy / _line->height());
3245 _line->start_drag_line (before, after, fraction);
3247 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3248 event->button.x + 10, event->button.y + 10);
3250 _editor->verbose_cursor()->show ();
3254 LineDrag::motion (GdkEvent* event, bool)
3256 double dy = _drags->current_pointer_y() - last_pointer_y();
3258 if (event->button.state & Keyboard::SecondaryModifier) {
3262 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3264 _cumulative_y_drag = cy - _fixed_grab_y;
3267 cy = min ((double) _line->height(), cy);
3269 double const fraction = 1.0 - (cy / _line->height());
3272 /* we are ignoring x position for this drag, so we can just pass in anything */
3273 _line->drag_motion (0, fraction, true, false, ignored);
3275 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3279 LineDrag::finished (GdkEvent* event, bool)
3281 motion (event, false);
3282 _line->end_drag (false, 0);
3283 _editor->session()->commit_reversible_command ();
3287 LineDrag::aborted (bool)
3292 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3295 _cumulative_x_drag (0)
3297 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3301 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3303 Drag::start_grab (event);
3305 _line = reinterpret_cast<Line*> (_item);
3308 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3310 double cx = event->button.x;
3311 double cy = event->button.y;
3313 _item->parent()->canvas_to_item (cx, cy);
3315 /* store grab start in parent frame */
3316 _region_view_grab_x = cx;
3318 _before = *(float*) _item->get_data ("position");
3320 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3322 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3326 FeatureLineDrag::motion (GdkEvent*, bool)
3328 double dx = _drags->current_pointer_x() - last_pointer_x();
3330 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3332 _cumulative_x_drag += dx;
3334 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3343 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3345 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3347 float *pos = new float;
3350 _line->set_data ("position", pos);
3356 FeatureLineDrag::finished (GdkEvent*, bool)
3358 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3359 _arv->update_transient(_before, _before);
3363 FeatureLineDrag::aborted (bool)
3368 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3370 , _vertical_only (false)
3372 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3376 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3378 Drag::start_grab (event);
3379 show_verbose_cursor_time (adjusted_current_frame (event));
3383 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3390 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3392 framepos_t grab = grab_frame ();
3393 if (Config->get_rubberbanding_snaps_to_grid ()) {
3394 _editor->snap_to_with_modifier (grab, event);
3397 /* base start and end on initial click position */
3407 if (_drags->current_pointer_y() < grab_y()) {
3408 y1 = _drags->current_pointer_y();
3411 y2 = _drags->current_pointer_y();
3416 if (start != end || y1 != y2) {
3418 double x1 = _editor->sample_to_pixel (start);
3419 double x2 = _editor->sample_to_pixel (end);
3421 _editor->rubberband_rect->set_x0 (x1);
3422 if (_vertical_only) {
3423 /* fixed 10 pixel width */
3424 _editor->rubberband_rect->set_x1 (x1 + 10);
3426 _editor->rubberband_rect->set_x1 (x2);
3429 _editor->rubberband_rect->set_y0 (y1);
3430 _editor->rubberband_rect->set_y1 (y2);
3432 _editor->rubberband_rect->show();
3433 _editor->rubberband_rect->raise_to_top();
3435 show_verbose_cursor_time (pf);
3437 do_select_things (event, true);
3442 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3447 if (grab_frame() < last_pointer_frame()) {
3449 x2 = last_pointer_frame ();
3452 x1 = last_pointer_frame ();
3458 if (_drags->current_pointer_y() < grab_y()) {
3459 y1 = _drags->current_pointer_y();
3462 y2 = _drags->current_pointer_y();
3466 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3470 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3472 if (movement_occurred) {
3474 motion (event, false);
3475 do_select_things (event, false);
3481 bool do_deselect = true;
3482 MidiTimeAxisView* mtv;
3484 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3486 if (_editor->selection->empty()) {
3487 /* nothing selected */
3488 add_midi_region (mtv);
3489 do_deselect = false;
3493 /* do not deselect if Primary or Tertiary (toggle-select or
3494 * extend-select are pressed.
3497 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3498 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3505 _editor->rubberband_rect->hide();
3509 RubberbandSelectDrag::aborted (bool)
3511 _editor->rubberband_rect->hide ();
3514 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3515 : RegionDrag (e, i, p, v)
3517 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3521 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3523 Drag::start_grab (event, cursor);
3525 show_verbose_cursor_time (adjusted_current_frame (event));
3529 TimeFXDrag::motion (GdkEvent* event, bool)
3531 RegionView* rv = _primary;
3532 StreamView* cv = rv->get_time_axis_view().view ();
3534 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3535 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3536 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3538 framepos_t const pf = adjusted_current_frame (event);
3540 if (pf > rv->region()->position()) {
3541 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3544 show_verbose_cursor_time (pf);
3548 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3550 _primary->get_time_axis_view().hide_timestretch ();
3552 if (!movement_occurred) {
3556 if (last_pointer_frame() < _primary->region()->position()) {
3557 /* backwards drag of the left edge - not usable */
3561 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3563 float percentage = (double) newlen / (double) _primary->region()->length();
3565 #ifndef USE_RUBBERBAND
3566 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3567 if (_primary->region()->data_type() == DataType::AUDIO) {
3568 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3572 if (!_editor->get_selection().regions.empty()) {
3573 /* primary will already be included in the selection, and edit
3574 group shared editing will propagate selection across
3575 equivalent regions, so just use the current region
3579 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3580 error << _("An error occurred while executing time stretch operation") << endmsg;
3586 TimeFXDrag::aborted (bool)
3588 _primary->get_time_axis_view().hide_timestretch ();
3591 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3594 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3598 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3600 Drag::start_grab (event);
3604 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3606 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3610 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3612 if (movement_occurred && _editor->session()) {
3613 /* make sure we stop */
3614 _editor->session()->request_transport_speed (0.0);
3619 ScrubDrag::aborted (bool)
3624 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3629 , _original_pointer_time_axis (-1)
3630 , _last_pointer_time_axis (-1)
3631 , _time_selection_at_start (!_editor->get_selection().time.empty())
3633 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3635 if (_time_selection_at_start) {
3636 start_at_start = _editor->get_selection().time.start();
3637 end_at_start = _editor->get_selection().time.end_frame();
3642 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3644 if (_editor->session() == 0) {
3648 Gdk::Cursor* cursor = 0;
3650 switch (_operation) {
3651 case CreateSelection:
3652 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3657 cursor = _editor->cursors()->selector;
3658 Drag::start_grab (event, cursor);
3661 case SelectionStartTrim:
3662 if (_editor->clicked_axisview) {
3663 _editor->clicked_axisview->order_selection_trims (_item, true);
3665 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3668 case SelectionEndTrim:
3669 if (_editor->clicked_axisview) {
3670 _editor->clicked_axisview->order_selection_trims (_item, false);
3672 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3676 Drag::start_grab (event, cursor);
3679 case SelectionExtend:
3680 Drag::start_grab (event, cursor);
3684 if (_operation == SelectionMove) {
3685 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3687 show_verbose_cursor_time (adjusted_current_frame (event));
3690 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3694 SelectionDrag::setup_pointer_frame_offset ()
3696 switch (_operation) {
3697 case CreateSelection:
3698 _pointer_frame_offset = 0;
3701 case SelectionStartTrim:
3703 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3706 case SelectionEndTrim:
3707 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3710 case SelectionExtend:
3716 SelectionDrag::motion (GdkEvent* event, bool first_move)
3718 framepos_t start = 0;
3720 framecnt_t length = 0;
3721 framecnt_t distance = 0;
3723 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3724 if (pending_time_axis.first == 0) {
3728 framepos_t const pending_position = adjusted_current_frame (event);
3730 /* only alter selection if things have changed */
3732 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3736 switch (_operation) {
3737 case CreateSelection:
3739 framepos_t grab = grab_frame ();
3742 grab = adjusted_current_frame (event, false);
3743 if (grab < pending_position) {
3744 _editor->snap_to (grab, -1);
3746 _editor->snap_to (grab, 1);
3750 if (pending_position < grab) {
3751 start = pending_position;
3754 end = pending_position;
3758 /* first drag: Either add to the selection
3759 or create a new selection
3765 /* adding to the selection */
3766 _editor->set_selected_track_as_side_effect (Selection::Add);
3767 //_editor->selection->add (_editor->clicked_axisview);
3768 _editor->clicked_selection = _editor->selection->add (start, end);
3773 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3774 //_editor->selection->set (_editor->clicked_axisview);
3775 _editor->set_selected_track_as_side_effect (Selection::Set);
3778 _editor->clicked_selection = _editor->selection->set (start, end);
3782 /* select the track that we're in */
3783 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3784 // _editor->set_selected_track_as_side_effect (Selection::Add);
3785 _editor->selection->add (pending_time_axis.first);
3786 _added_time_axes.push_back (pending_time_axis.first);
3789 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3790 tracks that we selected in the first place.
3793 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3794 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3796 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3797 while (i != _added_time_axes.end()) {
3799 list<TimeAxisView*>::iterator tmp = i;
3802 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3803 _editor->selection->remove (*i);
3804 _added_time_axes.remove (*i);
3813 case SelectionStartTrim:
3815 start = _editor->selection->time[_editor->clicked_selection].start;
3816 end = _editor->selection->time[_editor->clicked_selection].end;
3818 if (pending_position > end) {
3821 start = pending_position;
3825 case SelectionEndTrim:
3827 start = _editor->selection->time[_editor->clicked_selection].start;
3828 end = _editor->selection->time[_editor->clicked_selection].end;
3830 if (pending_position < start) {
3833 end = pending_position;
3840 start = _editor->selection->time[_editor->clicked_selection].start;
3841 end = _editor->selection->time[_editor->clicked_selection].end;
3843 length = end - start;
3844 distance = pending_position - start;
3845 start = pending_position;
3846 _editor->snap_to (start);
3848 end = start + length;
3852 case SelectionExtend:
3856 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
3857 _editor->start_canvas_autoscroll (1, 0);
3861 switch (_operation) {
3863 if (_time_selection_at_start) {
3864 _editor->selection->move_time (distance);
3868 _editor->selection->replace (_editor->clicked_selection, start, end);
3872 if (_operation == SelectionMove) {
3873 show_verbose_cursor_time(start);
3875 show_verbose_cursor_time(pending_position);
3880 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3882 Session* s = _editor->session();
3884 if (movement_occurred) {
3885 motion (event, false);
3886 /* XXX this is not object-oriented programming at all. ick */
3887 if (_editor->selection->time.consolidate()) {
3888 _editor->selection->TimeChanged ();
3891 /* XXX what if its a music time selection? */
3893 if ( s->get_play_range() && s->transport_rolling() ) {
3894 s->request_play_range (&_editor->selection->time, true);
3896 if (Config->get_always_play_range() && !s->transport_rolling()) {
3897 s->request_locate (_editor->get_selection().time.start());
3903 /* just a click, no pointer movement.
3906 if (_operation == SelectionExtend) {
3907 if (_time_selection_at_start) {
3908 framepos_t pos = adjusted_current_frame (event, false);
3909 framepos_t start = min (pos, start_at_start);
3910 framepos_t end = max (pos, end_at_start);
3911 _editor->selection->set (start, end);
3914 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3915 if (_editor->clicked_selection) {
3916 _editor->selection->remove (_editor->clicked_selection);
3919 if (!_editor->clicked_selection) {
3920 _editor->selection->clear_time();
3925 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3926 _editor->selection->set (_editor->clicked_axisview);
3929 if (s && s->get_play_range () && s->transport_rolling()) {
3930 s->request_stop (false, false);
3935 _editor->stop_canvas_autoscroll ();
3936 _editor->clicked_selection = 0;
3940 SelectionDrag::aborted (bool)
3945 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3950 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3952 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
3953 ArdourCanvas::Rect (0.0, 0.0, 0.0,
3954 physical_screen_height (_editor->get_window())));
3955 _drag_rect->hide ();
3957 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3958 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3962 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3964 if (_editor->session() == 0) {
3968 Gdk::Cursor* cursor = 0;
3970 if (!_editor->temp_location) {
3971 _editor->temp_location = new Location (*_editor->session());
3974 switch (_operation) {
3975 case CreateRangeMarker:
3976 case CreateTransportMarker:
3977 case CreateCDMarker:
3979 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3984 cursor = _editor->cursors()->selector;
3988 Drag::start_grab (event, cursor);
3990 show_verbose_cursor_time (adjusted_current_frame (event));
3994 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3996 framepos_t start = 0;
3998 ArdourCanvas::Rectangle *crect;
4000 switch (_operation) {
4001 case CreateRangeMarker:
4002 crect = _editor->range_bar_drag_rect;
4004 case CreateTransportMarker:
4005 crect = _editor->transport_bar_drag_rect;
4007 case CreateCDMarker:
4008 crect = _editor->cd_marker_bar_drag_rect;
4011 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4016 framepos_t const pf = adjusted_current_frame (event);
4018 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4019 framepos_t grab = grab_frame ();
4020 _editor->snap_to (grab);
4022 if (pf < grab_frame()) {
4030 /* first drag: Either add to the selection
4031 or create a new selection.
4036 _editor->temp_location->set (start, end);
4040 update_item (_editor->temp_location);
4042 //_drag_rect->raise_to_top();
4047 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
4048 _editor->start_canvas_autoscroll (1, 0);
4052 _editor->temp_location->set (start, end);
4054 double x1 = _editor->sample_to_pixel (start);
4055 double x2 = _editor->sample_to_pixel (end);
4059 update_item (_editor->temp_location);
4062 show_verbose_cursor_time (pf);
4067 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4069 Location * newloc = 0;
4073 if (movement_occurred) {
4074 motion (event, false);
4077 switch (_operation) {
4078 case CreateRangeMarker:
4079 case CreateCDMarker:
4081 _editor->begin_reversible_command (_("new range marker"));
4082 XMLNode &before = _editor->session()->locations()->get_state();
4083 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4084 if (_operation == CreateCDMarker) {
4085 flags = Location::IsRangeMarker | Location::IsCDMarker;
4086 _editor->cd_marker_bar_drag_rect->hide();
4089 flags = Location::IsRangeMarker;
4090 _editor->range_bar_drag_rect->hide();
4092 newloc = new Location (
4093 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4096 _editor->session()->locations()->add (newloc, true);
4097 XMLNode &after = _editor->session()->locations()->get_state();
4098 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4099 _editor->commit_reversible_command ();
4103 case CreateTransportMarker:
4104 // popup menu to pick loop or punch
4105 _editor->new_transport_marker_context_menu (&event->button, _item);
4109 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4111 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
4116 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4118 if (end == max_framepos) {
4119 end = _editor->session()->current_end_frame ();
4122 if (start == max_framepos) {
4123 start = _editor->session()->current_start_frame ();
4126 switch (_editor->mouse_mode) {
4128 /* find the two markers on either side and then make the selection from it */
4129 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4133 /* find the two markers on either side of the click and make the range out of it */
4134 _editor->selection->set (start, end);
4143 _editor->stop_canvas_autoscroll ();
4147 RangeMarkerBarDrag::aborted (bool)
4153 RangeMarkerBarDrag::update_item (Location* location)
4155 double const x1 = _editor->sample_to_pixel (location->start());
4156 double const x2 = _editor->sample_to_pixel (location->end());
4158 _drag_rect->set_x0 (x1);
4159 _drag_rect->set_x1 (x2);
4162 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4166 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4170 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4172 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4173 Drag::start_grab (event, _editor->cursors()->zoom_out);
4176 Drag::start_grab (event, _editor->cursors()->zoom_in);
4180 show_verbose_cursor_time (adjusted_current_frame (event));
4184 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4189 framepos_t const pf = adjusted_current_frame (event);
4191 framepos_t grab = grab_frame ();
4192 _editor->snap_to_with_modifier (grab, event);
4194 /* base start and end on initial click position */
4206 _editor->zoom_rect->show();
4207 _editor->zoom_rect->raise_to_top();
4210 _editor->reposition_zoom_rect(start, end);
4212 show_verbose_cursor_time (pf);
4217 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4219 if (movement_occurred) {
4220 motion (event, false);
4222 if (grab_frame() < last_pointer_frame()) {
4223 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4225 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4228 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4229 _editor->tav_zoom_step (_zoom_out);
4231 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4235 _editor->zoom_rect->hide();
4239 MouseZoomDrag::aborted (bool)
4241 _editor->zoom_rect->hide ();
4244 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4246 , _cumulative_dx (0)
4247 , _cumulative_dy (0)
4249 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4251 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4253 _region = &_primary->region_view ();
4254 _note_height = _region->midi_stream_view()->note_height ();
4258 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4260 Drag::start_grab (event);
4262 if (!(_was_selected = _primary->selected())) {
4264 /* tertiary-click means extend selection - we'll do that on button release,
4265 so don't add it here, because otherwise we make it hard to figure
4266 out the "extend-to" range.
4269 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4272 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4275 _region->note_selected (_primary, true);
4277 _region->unique_select (_primary);
4283 /** @return Current total drag x change in frames */
4285 NoteDrag::total_dx () const
4288 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4290 /* primary note time */
4291 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4293 /* new time of the primary note in session frames */
4294 frameoffset_t st = n + dx;
4296 framepos_t const rp = _region->region()->position ();
4298 /* prevent the note being dragged earlier than the region's position */
4301 /* snap and return corresponding delta */
4302 return _region->snap_frame_to_frame (st - rp) + rp - n;
4305 /** @return Current total drag y change in note number */
4307 NoteDrag::total_dy () const
4309 MidiStreamView* msv = _region->midi_stream_view ();
4310 double const y = _region->midi_view()->y_position ();
4311 /* new current note */
4312 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4314 n = max (msv->lowest_note(), n);
4315 n = min (msv->highest_note(), n);
4316 /* and work out delta */
4317 return n - msv->y_to_note (grab_y() - y);
4321 NoteDrag::motion (GdkEvent *, bool)
4323 /* Total change in x and y since the start of the drag */
4324 frameoffset_t const dx = total_dx ();
4325 int8_t const dy = total_dy ();
4327 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4328 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4329 double const tdy = -dy * _note_height - _cumulative_dy;
4332 _cumulative_dx += tdx;
4333 _cumulative_dy += tdy;
4335 int8_t note_delta = total_dy();
4337 _region->move_selection (tdx, tdy, note_delta);
4339 /* the new note value may be the same as the old one, but we
4340 * don't know what that means because the selection may have
4341 * involved more than one note and we might be doing something
4342 * odd with them. so show the note value anyway, always.
4346 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4348 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4349 (int) floor (new_note));
4351 show_verbose_cursor_text (buf);
4356 NoteDrag::finished (GdkEvent* ev, bool moved)
4359 /* no motion - select note */
4361 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4362 _editor->current_mouse_mode() == Editing::MouseDraw) {
4364 if (_was_selected) {
4365 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4367 _region->note_deselected (_primary);
4370 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4371 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4373 if (!extend && !add && _region->selection_size() > 1) {
4374 _region->unique_select (_primary);
4375 } else if (extend) {
4376 _region->note_selected (_primary, true, true);
4378 /* it was added during button press */
4383 _region->note_dropped (_primary, total_dx(), total_dy());
4388 NoteDrag::aborted (bool)
4393 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4394 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4395 : Drag (editor, atv->base_item ())
4397 , _nothing_to_drag (false)
4399 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4400 y_origin = atv->y_position();
4401 setup (atv->lines ());
4404 /** Make an AutomationRangeDrag for region gain lines */
4405 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4406 : Drag (editor, rv->get_canvas_group ())
4408 , _nothing_to_drag (false)
4410 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4412 list<boost::shared_ptr<AutomationLine> > lines;
4413 lines.push_back (rv->get_gain_line ());
4414 y_origin = rv->get_time_axis_view().y_position();
4418 /** @param lines AutomationLines to drag.
4419 * @param offset Offset from the session start to the points in the AutomationLines.
4422 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4424 /* find the lines that overlap the ranges being dragged */
4425 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4426 while (i != lines.end ()) {
4427 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4430 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4432 /* check this range against all the AudioRanges that we are using */
4433 list<AudioRange>::const_iterator k = _ranges.begin ();
4434 while (k != _ranges.end()) {
4435 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4441 /* add it to our list if it overlaps at all */
4442 if (k != _ranges.end()) {
4447 _lines.push_back (n);
4453 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4457 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4459 return 1.0 - ((global_y - y_origin) / line->height());
4463 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4465 Drag::start_grab (event, cursor);
4467 /* Get line states before we start changing things */
4468 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4469 i->state = &i->line->get_state ();
4470 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4473 if (_ranges.empty()) {
4475 /* No selected time ranges: drag all points */
4476 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4477 uint32_t const N = i->line->npoints ();
4478 for (uint32_t j = 0; j < N; ++j) {
4479 i->points.push_back (i->line->nth (j));
4485 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4487 framecnt_t const half = (i->start + i->end) / 2;
4489 /* find the line that this audio range starts in */
4490 list<Line>::iterator j = _lines.begin();
4491 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4495 if (j != _lines.end()) {
4496 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4498 /* j is the line that this audio range starts in; fade into it;
4499 64 samples length plucked out of thin air.
4502 framepos_t a = i->start + 64;
4507 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4508 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4510 the_list->add (p, the_list->eval (p));
4511 the_list->add (q, the_list->eval (q));
4514 /* same thing for the end */
4517 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4521 if (j != _lines.end()) {
4522 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4524 /* j is the line that this audio range starts in; fade out of it;
4525 64 samples length plucked out of thin air.
4528 framepos_t b = i->end - 64;
4533 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4534 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4536 the_list->add (p, the_list->eval (p));
4537 the_list->add (q, the_list->eval (q));
4541 _nothing_to_drag = true;
4543 /* Find all the points that should be dragged and put them in the relevant
4544 points lists in the Line structs.
4547 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4549 uint32_t const N = i->line->npoints ();
4550 for (uint32_t j = 0; j < N; ++j) {
4552 /* here's a control point on this line */
4553 ControlPoint* p = i->line->nth (j);
4554 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4556 /* see if it's inside a range */
4557 list<AudioRange>::const_iterator k = _ranges.begin ();
4558 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4562 if (k != _ranges.end()) {
4563 /* dragging this point */
4564 _nothing_to_drag = false;
4565 i->points.push_back (p);
4571 if (_nothing_to_drag) {
4575 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4576 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4581 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4583 if (_nothing_to_drag) {
4587 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4588 float const f = y_fraction (l->line, _drags->current_pointer_y());
4589 /* we are ignoring x position for this drag, so we can just pass in anything */
4591 l->line->drag_motion (0, f, true, false, ignored);
4592 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4597 AutomationRangeDrag::finished (GdkEvent* event, bool)
4599 if (_nothing_to_drag) {
4603 motion (event, false);
4604 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4605 i->line->end_drag (false, 0);
4608 _editor->session()->commit_reversible_command ();
4612 AutomationRangeDrag::aborted (bool)
4614 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4619 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4622 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4623 layer = v->region()->layer ();
4624 initial_y = v->get_canvas_group()->position().y;
4625 initial_playlist = v->region()->playlist ();
4626 initial_position = v->region()->position ();
4627 initial_end = v->region()->position () + v->region()->length ();
4630 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4631 : Drag (e, i->canvas_item ())
4634 , _cumulative_dx (0)
4636 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4637 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4642 PatchChangeDrag::motion (GdkEvent* ev, bool)
4644 framepos_t f = adjusted_current_frame (ev);
4645 boost::shared_ptr<Region> r = _region_view->region ();
4646 f = max (f, r->position ());
4647 f = min (f, r->last_frame ());
4649 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4650 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4651 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4652 _cumulative_dx = dxu;
4656 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4658 if (!movement_occurred) {
4662 boost::shared_ptr<Region> r (_region_view->region ());
4663 framepos_t f = adjusted_current_frame (ev);
4664 f = max (f, r->position ());
4665 f = min (f, r->last_frame ());
4667 _region_view->move_patch_change (
4669 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4674 PatchChangeDrag::aborted (bool)
4676 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4680 PatchChangeDrag::setup_pointer_frame_offset ()
4682 boost::shared_ptr<Region> region = _region_view->region ();
4683 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4686 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4687 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4694 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4696 framepos_t const p = _region_view->region()->position ();
4697 double const y = _region_view->midi_view()->y_position ();
4699 x1 = max ((framepos_t) 0, x1 - p);
4700 x2 = max ((framepos_t) 0, x2 - p);
4701 y1 = max (0.0, y1 - y);
4702 y2 = max (0.0, y2 - y);
4704 _region_view->update_drag_selection (
4705 _editor->sample_to_pixel (x1),
4706 _editor->sample_to_pixel (x2),
4709 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4714 MidiRubberbandSelectDrag::deselect_things ()
4719 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4720 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4723 _vertical_only = true;
4727 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4729 double const y = _region_view->midi_view()->y_position ();
4731 y1 = max (0.0, y1 - y);
4732 y2 = max (0.0, y2 - y);
4734 _region_view->update_vertical_drag_selection (
4737 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4742 MidiVerticalSelectDrag::deselect_things ()
4747 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4748 : RubberbandSelectDrag (e, i)
4754 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4756 if (drag_in_progress) {
4757 /* We just want to select things at the end of the drag, not during it */
4761 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4763 _editor->begin_reversible_command (_("rubberband selection"));
4764 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4765 _editor->commit_reversible_command ();
4769 EditorRubberbandSelectDrag::deselect_things ()
4771 if (!getenv("ARDOUR_SAE")) {
4772 _editor->selection->clear_tracks();
4774 _editor->selection->clear_regions();
4775 _editor->selection->clear_points ();
4776 _editor->selection->clear_lines ();
4779 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4787 NoteCreateDrag::~NoteCreateDrag ()
4793 NoteCreateDrag::grid_frames (framepos_t t) const
4796 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4801 return _region_view->region_beats_to_region_frames (grid_beats);
4805 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4807 Drag::start_grab (event, cursor);
4809 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4811 framepos_t pf = _drags->current_pointer_frame ();
4812 framecnt_t const g = grid_frames (pf);
4814 /* Hack so that we always snap to the note that we are over, instead of snapping
4815 to the next one if we're more than halfway through the one we're over.
4817 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4821 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4823 MidiStreamView* sv = _region_view->midi_stream_view ();
4824 double const x = _editor->sample_to_pixel (_note[0]);
4825 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4827 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4828 _drag_rect->set_outline_what (0xff);
4829 _drag_rect->set_outline_color (0xffffff99);
4830 _drag_rect->set_fill_color (0xffffff66);
4834 NoteCreateDrag::motion (GdkEvent* event, bool)
4836 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4837 double const x = _editor->sample_to_pixel (_note[1]);
4838 if (_note[1] > _note[0]) {
4839 _drag_rect->set_x1 (x);
4841 _drag_rect->set_x0 (x);
4846 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4848 if (!had_movement) {
4852 framepos_t const start = min (_note[0], _note[1]);
4853 framecnt_t length = (framecnt_t) fabs (_note[0] - _note[1]);
4855 framecnt_t const g = grid_frames (start);
4856 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4858 if (_editor->snap_mode() == SnapNormal && length < g) {
4859 length = g - one_tick;
4862 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4864 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
4868 NoteCreateDrag::y_to_region (double y) const
4871 _region_view->get_canvas_group()->canvas_to_item (x, y);
4876 NoteCreateDrag::aborted (bool)
4881 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4886 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
4890 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4892 Drag::start_grab (event, cursor);
4896 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4902 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4905 distance = _drags->current_pointer_x() - grab_x();
4906 len = ar->fade_in()->back()->when;
4908 distance = grab_x() - _drags->current_pointer_x();
4909 len = ar->fade_out()->back()->when;
4912 /* how long should it be ? */
4914 new_length = len + _editor->pixel_to_sample (distance);
4916 /* now check with the region that this is legal */
4918 new_length = ar->verify_xfade_bounds (new_length, start);
4921 arv->reset_fade_in_shape_width (ar, new_length);
4923 arv->reset_fade_out_shape_width (ar, new_length);
4928 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
4934 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4937 distance = _drags->current_pointer_x() - grab_x();
4938 len = ar->fade_in()->back()->when;
4940 distance = grab_x() - _drags->current_pointer_x();
4941 len = ar->fade_out()->back()->when;
4944 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
4946 _editor->begin_reversible_command ("xfade trim");
4947 ar->playlist()->clear_owned_changes ();
4950 ar->set_fade_in_length (new_length);
4952 ar->set_fade_out_length (new_length);
4955 /* Adjusting the xfade may affect other regions in the playlist, so we need
4956 to get undo Commands from the whole playlist rather than just the
4960 vector<Command*> cmds;
4961 ar->playlist()->rdiff (cmds);
4962 _editor->session()->add_commands (cmds);
4963 _editor->commit_reversible_command ();
4968 CrossfadeEdgeDrag::aborted (bool)
4971 arv->redraw_start_xfade ();
4973 arv->redraw_end_xfade ();