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.
20 #define __STDC_LIMIT_MACROS 1
22 #include "pbd/memento_command.h"
23 #include "pbd/basename.h"
24 #include "pbd/stateful_diff_command.h"
25 #include "ardour/session.h"
26 #include "ardour/dB.h"
27 #include "ardour/region_factory.h"
31 #include "audio_region_view.h"
32 #include "midi_region_view.h"
33 #include "ardour_ui.h"
34 #include "gui_thread.h"
35 #include "control_point.h"
37 #include "region_gain_line.h"
38 #include "editor_drag.h"
39 #include "audio_time_axis.h"
40 #include "midi_time_axis.h"
41 #include "canvas-note.h"
42 #include "selection.h"
43 #include "midi_selection.h"
44 #include "automation_time_axis.h"
48 using namespace ARDOUR;
51 using namespace Editing;
52 using namespace ArdourCanvas;
54 using Gtkmm2ext::Keyboard;
56 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
58 DragManager::DragManager (Editor* e)
61 , _current_pointer_frame (0)
66 DragManager::~DragManager ()
71 /** Call abort for each active drag */
77 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
88 DragManager::add (Drag* d)
90 d->set_manager (this);
95 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
97 assert (_drags.empty ());
98 d->set_manager (this);
104 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
106 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
108 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
109 (*i)->start_grab (e, c);
113 /** Call end_grab for each active drag.
114 * @return true if any drag reported movement having occurred.
117 DragManager::end_grab (GdkEvent* e)
122 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
123 bool const t = (*i)->end_grab (e);
138 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
142 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
144 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
145 bool const t = (*i)->motion_handler (e, from_autoscroll);
156 DragManager::have_item (ArdourCanvas::Item* i) const
158 list<Drag*>::const_iterator j = _drags.begin ();
159 while (j != _drags.end() && (*j)->item () != i) {
163 return j != _drags.end ();
166 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
169 , _pointer_frame_offset (0)
170 , _move_threshold_passed (false)
171 , _raw_grab_frame (0)
173 , _last_pointer_frame (0)
179 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
185 cursor = _editor->which_grabber_cursor ();
188 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
192 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
195 cursor = _editor->which_grabber_cursor ();
198 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
200 if (Keyboard::is_button2_event (&event->button)) {
201 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
202 _y_constrained = true;
203 _x_constrained = false;
205 _y_constrained = false;
206 _x_constrained = true;
209 _x_constrained = false;
210 _y_constrained = false;
213 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
214 _grab_frame = adjusted_frame (_raw_grab_frame, event);
215 _last_pointer_frame = _grab_frame;
216 _last_pointer_x = _grab_x;
217 _last_pointer_y = _grab_y;
219 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
223 if (_editor->session() && _editor->session()->transport_rolling()) {
226 _was_rolling = false;
229 switch (_editor->snap_type()) {
230 case SnapToRegionStart:
231 case SnapToRegionEnd:
232 case SnapToRegionSync:
233 case SnapToRegionBoundary:
234 _editor->build_region_boundary_cache ();
241 /** Call to end a drag `successfully'. Ungrabs item and calls
242 * subclass' finished() method.
244 * @param event GDK event, or 0.
245 * @return true if some movement occurred, otherwise false.
248 Drag::end_grab (GdkEvent* event)
250 _editor->stop_canvas_autoscroll ();
252 _item->ungrab (event ? event->button.time : 0);
254 finished (event, _move_threshold_passed);
256 _editor->hide_verbose_canvas_cursor();
258 return _move_threshold_passed;
262 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
266 if (f > _pointer_frame_offset) {
267 pos = f - _pointer_frame_offset;
271 _editor->snap_to_with_modifier (pos, event);
278 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
280 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
284 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
286 /* check to see if we have moved in any way that matters since the last motion event */
287 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
288 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
292 pair<framecnt_t, int> const threshold = move_threshold ();
294 bool const old_move_threshold_passed = _move_threshold_passed;
296 if (!from_autoscroll && !_move_threshold_passed) {
298 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
299 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
301 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
304 if (active (_editor->mouse_mode) && _move_threshold_passed) {
306 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
307 if (!from_autoscroll) {
308 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
311 motion (event, _move_threshold_passed != old_move_threshold_passed);
313 _last_pointer_x = _drags->current_pointer_x ();
314 _last_pointer_y = _drags->current_pointer_y ();
315 _last_pointer_frame = adjusted_current_frame (event);
323 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
333 _editor->stop_canvas_autoscroll ();
334 _editor->hide_verbose_canvas_cursor ();
337 struct EditorOrderTimeAxisViewSorter {
338 bool operator() (TimeAxisView* a, TimeAxisView* b) {
339 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
340 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
342 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
346 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
350 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
352 /* Make a list of non-hidden tracks to refer to during the drag */
354 TrackViewList track_views = _editor->track_views;
355 track_views.sort (EditorOrderTimeAxisViewSorter ());
357 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
358 if (!(*i)->hidden()) {
360 _time_axis_views.push_back (*i);
362 TimeAxisView::Children children_list = (*i)->get_child_list ();
363 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
364 _time_axis_views.push_back (j->get());
369 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
370 _views.push_back (DraggingView (*i, this));
373 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
377 RegionDrag::region_going_away (RegionView* v)
379 list<DraggingView>::iterator i = _views.begin ();
380 while (i != _views.end() && i->view != v) {
384 if (i != _views.end()) {
389 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
391 RegionDrag::find_time_axis_view (TimeAxisView* t) const
394 int const N = _time_axis_views.size ();
395 while (i < N && _time_axis_views[i] != t) {
406 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
407 : RegionDrag (e, i, p, v),
416 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
418 Drag::start_grab (event, cursor);
420 _editor->show_verbose_time_cursor (_last_frame_position, 10);
422 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
423 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
424 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
428 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
430 /* compute the amount of pointer motion in frames, and where
431 the region would be if we moved it by that much.
433 *pending_region_position = adjusted_current_frame (event);
435 framepos_t sync_frame;
436 framecnt_t sync_offset;
439 sync_offset = _primary->region()->sync_offset (sync_dir);
441 /* we don't handle a sync point that lies before zero.
443 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
445 sync_frame = *pending_region_position + (sync_dir*sync_offset);
447 _editor->snap_to_with_modifier (sync_frame, event);
449 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
452 *pending_region_position = _last_frame_position;
455 if (*pending_region_position > max_frames - _primary->region()->length()) {
456 *pending_region_position = _last_frame_position;
461 /* in locked edit mode, reverse the usual meaning of _x_constrained */
462 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
464 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
466 /* x movement since last time */
467 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
469 /* total x movement */
470 framecnt_t total_dx = *pending_region_position;
471 if (regions_came_from_canvas()) {
472 total_dx = total_dx - grab_frame () + _pointer_frame_offset;
475 /* check that no regions have gone off the start of the session */
476 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
477 if ((i->view->region()->position() + total_dx) < 0) {
479 *pending_region_position = _last_frame_position;
484 _last_frame_position = *pending_region_position;
491 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
493 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
494 int const n = i->time_axis_view + delta_track;
495 if (n < 0 || n >= int (_time_axis_views.size())) {
496 /* off the top or bottom track */
500 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
501 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
502 /* not a track, or the wrong type */
506 int const l = i->layer + delta_layer;
507 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
508 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
509 If it has, the layers will be munged later anyway, so it's ok.
515 /* all regions being dragged are ok with this change */
520 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
522 /* Find the TimeAxisView that the pointer is now over */
523 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
525 /* Bail early if we're not over a track */
526 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
527 if (!rtv || !rtv->is_track()) {
528 _editor->hide_verbose_canvas_cursor ();
532 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
534 /* Here's the current pointer position in terms of time axis view and layer */
535 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
536 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
538 /* Work out the change in x */
539 framepos_t pending_region_position;
540 double const x_delta = compute_x_delta (event, &pending_region_position);
542 /* Work out the change in y */
543 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
544 int delta_layer = current_pointer_layer - _last_pointer_layer;
546 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
547 /* this y movement is not allowed, so do no y movement this time */
548 delta_time_axis_view = 0;
552 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
553 /* haven't reached next snap point, and we're not switching
554 trackviews nor layers. nothing to do.
559 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
561 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
563 RegionView* rv = i->view;
565 if (rv->region()->locked()) {
571 /* here we are calculating the y distance from the
572 top of the first track view to the top of the region
573 area of the track view that we're working on */
575 /* this x value is just a dummy value so that we have something
580 /* distance from the top of this track view to the region area
581 of our track view is always 1 */
585 /* convert to world coordinates, ie distance from the top of
588 rv->get_canvas_frame()->i2w (ix1, iy1);
590 /* compensate for the ruler section and the vertical scrollbar position */
591 iy1 += _editor->get_trackview_group_vertical_offset ();
593 // hide any dependent views
595 rv->get_time_axis_view().hide_dependent_views (*rv);
598 reparent to a non scrolling group so that we can keep the
599 region selection above all time axis views.
600 reparenting means we have to move the rv as the two
601 parent groups have different coordinates.
604 rv->get_canvas_group()->property_y() = iy1 - 1;
605 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
607 rv->fake_set_opaque (true);
610 /* Work out the change in y position of this region view */
614 /* If we have moved tracks, we'll fudge the layer delta so that the
615 region gets moved back onto layer 0 on its new track; this avoids
616 confusion when dragging regions from non-zero layers onto different
619 int this_delta_layer = delta_layer;
620 if (delta_time_axis_view != 0) {
621 this_delta_layer = - i->layer;
624 /* Move this region to layer 0 on its old track */
625 StreamView* lv = _time_axis_views[i->time_axis_view]->view ();
626 if (lv->layer_display() == Stacked) {
627 y_delta -= (lv->layers() - i->layer - 1) * lv->child_height ();
630 /* Now move it to its right layer on the current track */
631 StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view ();
632 if (cv->layer_display() == Stacked) {
633 y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height ();
637 if (delta_time_axis_view > 0) {
638 for (int j = 0; j < delta_time_axis_view; ++j) {
639 y_delta += _time_axis_views[i->time_axis_view + j]->current_height ();
642 /* start by subtracting the height of the track above where we are now */
643 for (int j = 1; j <= -delta_time_axis_view; ++j) {
644 y_delta -= _time_axis_views[i->time_axis_view - j]->current_height ();
649 rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ());
651 /* Update the DraggingView */
652 i->time_axis_view += delta_time_axis_view;
653 i->layer += this_delta_layer;
656 _editor->mouse_brush_insert_region (rv, pending_region_position);
658 rv->move (x_delta, y_delta);
661 } /* foreach region */
663 _total_x_delta += x_delta;
666 _editor->cursor_group->raise_to_top();
669 if (x_delta != 0 && !_brushing) {
670 _editor->show_verbose_time_cursor (_last_frame_position, 10);
673 _last_pointer_time_axis_view += delta_time_axis_view;
674 _last_pointer_layer += delta_layer;
678 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
680 if (_copy && first_move) {
682 /* duplicate the regionview(s) and region(s) */
684 list<DraggingView> new_regionviews;
686 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
688 RegionView* rv = i->view;
689 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
690 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
692 const boost::shared_ptr<const Region> original = rv->region();
693 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
694 region_copy->set_position (original->position(), this);
698 boost::shared_ptr<AudioRegion> audioregion_copy
699 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
701 nrv = new AudioRegionView (*arv, audioregion_copy);
703 boost::shared_ptr<MidiRegion> midiregion_copy
704 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
705 nrv = new MidiRegionView (*mrv, midiregion_copy);
710 nrv->get_canvas_group()->show ();
711 new_regionviews.push_back (DraggingView (nrv, this));
713 /* swap _primary to the copy */
715 if (rv == _primary) {
719 /* ..and deselect the one we copied */
721 rv->set_selected (false);
724 if (!new_regionviews.empty()) {
726 /* reflect the fact that we are dragging the copies */
728 _views = new_regionviews;
730 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
733 sync the canvas to what we think is its current state
734 without it, the canvas seems to
735 "forget" to update properly after the upcoming reparent()
736 ..only if the mouse is in rapid motion at the time of the grab.
737 something to do with regionview creation taking so long?
739 _editor->update_canvas_now();
743 RegionMotionDrag::motion (event, first_move);
747 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
749 if (!movement_occurred) {
754 /* reverse this here so that we have the correct logic to finalize
758 if (Config->get_edit_mode() == Lock) {
759 _x_constrained = !_x_constrained;
762 bool const changed_position = (_last_frame_position != _primary->region()->position());
763 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
764 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
766 _editor->update_canvas_now ();
788 RegionMoveDrag::finished_copy (
789 bool const changed_position,
790 bool const changed_tracks,
791 framecnt_t const drag_delta
794 RegionSelection new_views;
795 PlaylistSet modified_playlists;
796 list<RegionView*> views_to_delete;
799 /* all changes were made during motion event handlers */
801 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
805 _editor->commit_reversible_command ();
809 if (_x_constrained) {
810 _editor->begin_reversible_command (_("fixed time region copy"));
812 _editor->begin_reversible_command (_("region copy"));
815 /* insert the regions into their new playlists */
816 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
818 if (i->view->region()->locked()) {
824 if (changed_position && !_x_constrained) {
825 where = i->view->region()->position() - drag_delta;
827 where = i->view->region()->position();
830 RegionView* new_view = insert_region_into_playlist (
831 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
838 new_views.push_back (new_view);
840 /* we don't need the copied RegionView any more */
841 views_to_delete.push_back (i->view);
844 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
845 because when views are deleted they are automagically removed from _views, which messes
848 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
852 /* If we've created new regions either by copying or moving
853 to a new track, we want to replace the old selection with the new ones
856 if (new_views.size() > 0) {
857 _editor->selection->set (new_views);
860 /* write commands for the accumulated diffs for all our modified playlists */
861 add_stateful_diff_commands_for_playlists (modified_playlists);
863 _editor->commit_reversible_command ();
867 RegionMoveDrag::finished_no_copy (
868 bool const changed_position,
869 bool const changed_tracks,
870 framecnt_t const drag_delta
873 RegionSelection new_views;
874 PlaylistSet modified_playlists;
875 PlaylistSet frozen_playlists;
878 /* all changes were made during motion event handlers */
879 _editor->commit_reversible_command ();
883 if (_x_constrained) {
884 _editor->begin_reversible_command (_("fixed time region drag"));
886 _editor->begin_reversible_command (_("region drag"));
889 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
891 RegionView* rv = i->view;
893 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
894 layer_t const dest_layer = i->layer;
896 if (rv->region()->locked()) {
903 if (changed_position && !_x_constrained) {
904 where = rv->region()->position() - drag_delta;
906 where = rv->region()->position();
909 if (changed_tracks) {
911 /* insert into new playlist */
913 RegionView* new_view = insert_region_into_playlist (
914 RegionFactory::create (rv->region ()), dest_rtv, dest_layer, where, modified_playlists
922 new_views.push_back (new_view);
924 /* remove from old playlist */
926 /* the region that used to be in the old playlist is not
927 moved to the new one - we use a copy of it. as a result,
928 any existing editor for the region should no longer be
931 rv->hide_region_editor();
932 rv->fake_set_opaque (false);
934 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
938 rv->region()->clear_changes ();
941 motion on the same track. plonk the previously reparented region
942 back to its original canvas group (its streamview).
943 No need to do anything for copies as they are fake regions which will be deleted.
946 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
947 rv->get_canvas_group()->property_y() = i->initial_y;
948 rv->get_time_axis_view().reveal_dependent_views (*rv);
950 /* just change the model */
952 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
954 if (dest_rtv->view()->layer_display() == Stacked) {
955 rv->region()->set_layer (dest_layer);
956 rv->region()->set_pending_explicit_relayer (true);
959 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
961 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
967 /* this movement may result in a crossfade being modified, so we need to get undo
968 data from the playlist as well as the region.
971 r = modified_playlists.insert (playlist);
973 playlist->clear_changes ();
976 rv->region()->set_position (where, (void*) this);
978 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
981 if (changed_tracks) {
983 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
984 was selected in all of them, then removing it from a playlist will have removed all
985 trace of it from _views (i.e. there were N regions selected, we removed 1,
986 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
987 corresponding regionview, and _views is now empty).
989 This could have invalidated any and all iterators into _views.
991 The heuristic we use here is: if the region selection is empty, break out of the loop
992 here. if the region selection is not empty, then restart the loop because we know that
993 we must have removed at least the region(view) we've just been working on as well as any
994 that we processed on previous iterations.
996 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1001 if (_views.empty()) {
1012 /* If we've created new regions either by copying or moving
1013 to a new track, we want to replace the old selection with the new ones
1016 if (new_views.size() > 0) {
1017 _editor->selection->set (new_views);
1020 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1024 /* write commands for the accumulated diffs for all our modified playlists */
1025 add_stateful_diff_commands_for_playlists (modified_playlists);
1027 _editor->commit_reversible_command ();
1030 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1031 * @param region Region to remove.
1032 * @param playlist playlist To remove from.
1033 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1034 * that clear_changes () is only called once per playlist.
1037 RegionMoveDrag::remove_region_from_playlist (
1038 boost::shared_ptr<Region> region,
1039 boost::shared_ptr<Playlist> playlist,
1040 PlaylistSet& modified_playlists
1043 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1046 playlist->clear_changes ();
1049 playlist->remove_region (region);
1053 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1054 * clearing the playlist's diff history first if necessary.
1055 * @param region Region to insert.
1056 * @param dest_rtv Destination RouteTimeAxisView.
1057 * @param dest_layer Destination layer.
1058 * @param where Destination position.
1059 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1060 * that clear_changes () is only called once per playlist.
1061 * @return New RegionView, or 0 if no insert was performed.
1064 RegionMoveDrag::insert_region_into_playlist (
1065 boost::shared_ptr<Region> region,
1066 RouteTimeAxisView* dest_rtv,
1069 PlaylistSet& modified_playlists
1072 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1073 if (!dest_playlist) {
1077 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1078 _new_region_view = 0;
1079 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1081 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1082 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1084 dest_playlist->clear_changes ();
1087 dest_playlist->add_region (region, where);
1089 if (dest_rtv->view()->layer_display() == Stacked) {
1090 region->set_layer (dest_layer);
1091 region->set_pending_explicit_relayer (true);
1096 assert (_new_region_view);
1098 return _new_region_view;
1102 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1104 _new_region_view = rv;
1108 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1110 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1111 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1113 _editor->session()->add_command (new StatefulDiffCommand (*i));
1122 RegionMoveDrag::aborted ()
1126 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1133 RegionMotionDrag::aborted ();
1138 RegionMotionDrag::aborted ()
1140 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1141 RegionView* rv = i->view;
1142 TimeAxisView* tv = &(rv->get_time_axis_view ());
1143 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1145 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1146 rv->get_canvas_group()->property_y() = 0;
1147 rv->get_time_axis_view().reveal_dependent_views (*rv);
1148 rv->fake_set_opaque (false);
1149 rv->move (-_total_x_delta, 0);
1150 rv->set_height (rtv->view()->child_height ());
1153 _editor->update_canvas_now ();
1156 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1157 : RegionMotionDrag (e, i, p, v, b),
1160 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1163 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1164 if (rtv && rtv->is_track()) {
1165 speed = rtv->track()->speed ();
1168 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1172 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1174 RegionMotionDrag::start_grab (event, c);
1176 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1179 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1180 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1182 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1184 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1185 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1187 _primary = v->view()->create_region_view (r, false, false);
1189 _primary->get_canvas_group()->show ();
1190 _primary->set_position (pos, 0);
1191 _views.push_back (DraggingView (_primary, this));
1193 _last_frame_position = pos;
1195 _item = _primary->get_canvas_group ();
1199 RegionInsertDrag::finished (GdkEvent *, bool)
1201 _editor->update_canvas_now ();
1203 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1205 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1206 _primary->get_canvas_group()->property_y() = 0;
1208 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1210 _editor->begin_reversible_command (_("insert region"));
1211 playlist->clear_changes ();
1212 playlist->add_region (_primary->region (), _last_frame_position);
1213 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1214 _editor->commit_reversible_command ();
1222 RegionInsertDrag::aborted ()
1229 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1230 : RegionMoveDrag (e, i, p, v, false, false)
1232 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1235 struct RegionSelectionByPosition {
1236 bool operator() (RegionView*a, RegionView* b) {
1237 return a->region()->position () < b->region()->position();
1242 RegionSpliceDrag::motion (GdkEvent* event, bool)
1244 /* Which trackview is this ? */
1246 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1247 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1248 layer_t layer = tvp.second;
1250 if (tv && tv->layer_display() == Overlaid) {
1254 /* The region motion is only processed if the pointer is over
1258 if (!tv || !tv->is_track()) {
1259 /* To make sure we hide the verbose canvas cursor when the mouse is
1260 not held over and audiotrack.
1262 _editor->hide_verbose_canvas_cursor ();
1268 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1274 RegionSelection copy (_editor->selection->regions);
1276 RegionSelectionByPosition cmp;
1279 framepos_t const pf = adjusted_current_frame (event);
1281 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1283 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1289 boost::shared_ptr<Playlist> playlist;
1291 if ((playlist = atv->playlist()) == 0) {
1295 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1300 if (pf < (*i)->region()->last_frame() + 1) {
1304 if (pf > (*i)->region()->first_frame()) {
1310 playlist->shuffle ((*i)->region(), dir);
1315 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1317 RegionMoveDrag::finished (event, movement_occurred);
1321 RegionSpliceDrag::aborted ()
1326 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1328 _view (dynamic_cast<MidiTimeAxisView*> (v))
1330 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1336 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1339 /* don't use a zero-length region otherwise its region view will be hidden when it is created */
1340 _region = _view->add_region (grab_frame(), 1, false);
1342 framepos_t const f = adjusted_current_frame (event);
1343 if (f < grab_frame()) {
1344 _region->set_position (f, this);
1347 /* again, don't use a zero-length region (see above) */
1348 framecnt_t const len = abs (f - grab_frame ());
1349 _region->set_length (len < 1 ? 1 : len, this);
1354 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1356 if (movement_occurred) {
1357 _editor->commit_reversible_command ();
1362 RegionCreateDrag::aborted ()
1367 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1371 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1375 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1377 Gdk::Cursor* cursor;
1378 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1380 Drag::start_grab (event);
1382 region = &cnote->region_view();
1384 double const region_start = region->get_position_pixels();
1385 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1387 if (grab_x() <= middle_point) {
1388 cursor = _editor->left_side_trim_cursor;
1391 cursor = _editor->right_side_trim_cursor;
1395 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1397 if (event->motion.state & Keyboard::PrimaryModifier) {
1403 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1405 if (ms.size() > 1) {
1406 /* has to be relative, may make no sense otherwise */
1410 /* select this note; if it is already selected, preserve the existing selection,
1411 otherwise make this note the only one selected.
1413 region->note_selected (cnote, cnote->selected ());
1415 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1416 MidiRegionSelection::iterator next;
1419 (*r)->begin_resizing (at_front);
1425 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1427 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1428 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1429 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1434 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1436 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1437 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1438 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1443 NoteResizeDrag::aborted ()
1448 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1451 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1455 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1461 RegionGainDrag::finished (GdkEvent *, bool)
1467 RegionGainDrag::aborted ()
1472 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1473 : RegionDrag (e, i, p, v)
1474 , _have_transaction (false)
1476 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1480 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1483 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1484 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1486 if (tv && tv->is_track()) {
1487 speed = tv->track()->speed();
1490 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1491 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1492 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1494 framepos_t const pf = adjusted_current_frame (event);
1496 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1497 _operation = ContentsTrim;
1498 Drag::start_grab (event, _editor->trimmer_cursor);
1500 /* These will get overridden for a point trim.*/
1501 if (pf < (region_start + region_length/2)) {
1502 /* closer to start */
1503 _operation = StartTrim;
1504 Drag::start_grab (event, _editor->left_side_trim_cursor);
1507 _operation = EndTrim;
1508 Drag::start_grab (event, _editor->right_side_trim_cursor);
1512 switch (_operation) {
1514 _editor->show_verbose_time_cursor (region_start, 10);
1517 _editor->show_verbose_time_cursor (region_end, 10);
1520 _editor->show_verbose_time_cursor (pf, 10);
1526 TrimDrag::motion (GdkEvent* event, bool first_move)
1528 RegionView* rv = _primary;
1530 /* snap modifier works differently here..
1531 its current state has to be passed to the
1532 various trim functions in order to work properly
1536 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1537 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1538 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1540 if (tv && tv->is_track()) {
1541 speed = tv->track()->speed();
1544 framepos_t const pf = adjusted_current_frame (event);
1550 switch (_operation) {
1552 trim_type = "Region start trim";
1555 trim_type = "Region end trim";
1558 trim_type = "Region content trim";
1562 _editor->begin_reversible_command (trim_type);
1563 _have_transaction = true;
1565 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1566 RegionView* rv = i->view;
1567 rv->fake_set_opaque(false);
1568 rv->enable_display (false);
1569 rv->region()->clear_changes ();
1570 rv->region()->suspend_property_changes ();
1572 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1575 arv->temporarily_hide_envelope ();
1578 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1579 insert_result = _editor->motion_frozen_playlists.insert (pl);
1581 if (insert_result.second) {
1587 bool non_overlap_trim = false;
1589 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1590 non_overlap_trim = true;
1593 switch (_operation) {
1595 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1596 _editor->single_start_trim (*i->view, pf, non_overlap_trim);
1601 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1602 _editor->single_end_trim (*i->view, pf, non_overlap_trim);
1608 bool swap_direction = false;
1610 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1611 swap_direction = true;
1614 framecnt_t frame_delta = 0;
1616 bool left_direction = false;
1617 if (last_pointer_frame() > pf) {
1618 left_direction = true;
1621 if (left_direction) {
1622 frame_delta = (last_pointer_frame() - pf);
1624 frame_delta = (pf - last_pointer_frame());
1627 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1628 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1634 switch (_operation) {
1636 _editor->show_verbose_time_cursor((framepos_t) (rv->region()->position()/speed), 10);
1639 _editor->show_verbose_time_cursor((framepos_t) (rv->region()->last_frame()/speed), 10);
1642 _editor->show_verbose_time_cursor (pf, 10);
1649 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1651 if (movement_occurred) {
1652 motion (event, false);
1654 if (!_editor->selection->selected (_primary)) {
1655 _editor->thaw_region_after_trim (*_primary);
1658 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1659 _editor->thaw_region_after_trim (*i->view);
1660 i->view->enable_display (true);
1661 i->view->fake_set_opaque (true);
1662 if (_have_transaction) {
1663 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1667 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1671 _editor->motion_frozen_playlists.clear ();
1673 if (_have_transaction) {
1674 _editor->commit_reversible_command();
1678 /* no mouse movement */
1679 _editor->point_trim (event, adjusted_current_frame (event));
1684 TrimDrag::aborted ()
1686 /* Our motion method is changing model state, so use the Undo system
1687 to cancel. Perhaps not ideal, as this will leave an Undo point
1688 behind which may be slightly odd from the user's point of view.
1693 if (_have_transaction) {
1698 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1702 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1704 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1709 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1712 // create a dummy marker for visual representation of moving the copy.
1713 // The actual copying is not done before we reach the finish callback.
1715 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1716 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1717 *new MeterSection (_marker->meter()));
1719 _item = &new_marker->the_item ();
1720 _marker = new_marker;
1724 MetricSection& section (_marker->meter());
1726 if (!section.movable()) {
1732 Drag::start_grab (event, cursor);
1734 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1736 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1740 MeterMarkerDrag::motion (GdkEvent* event, bool)
1742 framepos_t const pf = adjusted_current_frame (event);
1744 _marker->set_position (pf);
1746 _editor->show_verbose_time_cursor (pf, 10);
1750 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1752 if (!movement_occurred) {
1756 motion (event, false);
1760 TempoMap& map (_editor->session()->tempo_map());
1761 map.bbt_time (last_pointer_frame(), when);
1763 if (_copy == true) {
1764 _editor->begin_reversible_command (_("copy meter mark"));
1765 XMLNode &before = map.get_state();
1766 map.add_meter (_marker->meter(), when);
1767 XMLNode &after = map.get_state();
1768 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1769 _editor->commit_reversible_command ();
1771 // delete the dummy marker we used for visual representation of copying.
1772 // a new visual marker will show up automatically.
1775 _editor->begin_reversible_command (_("move meter mark"));
1776 XMLNode &before = map.get_state();
1777 map.move_meter (_marker->meter(), when);
1778 XMLNode &after = map.get_state();
1779 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1780 _editor->commit_reversible_command ();
1785 MeterMarkerDrag::aborted ()
1787 _marker->set_position (_marker->meter().frame ());
1790 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1794 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1796 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1801 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1806 // create a dummy marker for visual representation of moving the copy.
1807 // The actual copying is not done before we reach the finish callback.
1809 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1810 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
1811 *new TempoSection (_marker->tempo()));
1813 _item = &new_marker->the_item ();
1814 _marker = new_marker;
1818 MetricSection& section (_marker->tempo());
1820 if (!section.movable()) {
1825 Drag::start_grab (event, cursor);
1827 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1828 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1832 TempoMarkerDrag::motion (GdkEvent* event, bool)
1834 framepos_t const pf = adjusted_current_frame (event);
1835 _marker->set_position (pf);
1836 _editor->show_verbose_time_cursor (pf, 10);
1840 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1842 if (!movement_occurred) {
1846 motion (event, false);
1850 TempoMap& map (_editor->session()->tempo_map());
1851 map.bbt_time (last_pointer_frame(), when);
1853 if (_copy == true) {
1854 _editor->begin_reversible_command (_("copy tempo mark"));
1855 XMLNode &before = map.get_state();
1856 map.add_tempo (_marker->tempo(), when);
1857 XMLNode &after = map.get_state();
1858 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1859 _editor->commit_reversible_command ();
1861 // delete the dummy marker we used for visual representation of copying.
1862 // a new visual marker will show up automatically.
1865 _editor->begin_reversible_command (_("move tempo mark"));
1866 XMLNode &before = map.get_state();
1867 map.move_tempo (_marker->tempo(), when);
1868 XMLNode &after = map.get_state();
1869 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1870 _editor->commit_reversible_command ();
1875 TempoMarkerDrag::aborted ()
1877 _marker->set_position (_marker->tempo().frame());
1880 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1884 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
1886 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1891 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1893 Drag::start_grab (event, c);
1897 framepos_t where = _editor->event_frame (event, 0, 0);
1899 _editor->snap_to_with_modifier (where, event);
1900 _editor->playhead_cursor->set_position (where);
1904 if (_cursor == _editor->playhead_cursor) {
1905 _editor->_dragging_playhead = true;
1907 Session* s = _editor->session ();
1910 if (_was_rolling && _stop) {
1914 if (s->is_auditioning()) {
1915 s->cancel_audition ();
1918 s->request_suspend_timecode_transmission ();
1920 if (s->timecode_transmission_suspended ()) {
1921 framepos_t const f = _editor->playhead_cursor->current_frame;
1922 s->send_mmc_locate (f);
1923 s->send_full_time_code (f);
1928 _pointer_frame_offset = raw_grab_frame() - _cursor->current_frame;
1930 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1934 CursorDrag::motion (GdkEvent* event, bool)
1936 framepos_t const adjusted_frame = adjusted_current_frame (event);
1938 if (adjusted_frame == last_pointer_frame()) {
1942 _cursor->set_position (adjusted_frame);
1944 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1946 Session* s = _editor->session ();
1947 if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
1948 framepos_t const f = _editor->playhead_cursor->current_frame;
1949 s->send_mmc_locate (f);
1950 s->send_full_time_code (f);
1955 _editor->update_canvas_now ();
1957 _editor->UpdateAllTransportClocks (_cursor->current_frame);
1961 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
1963 _editor->_dragging_playhead = false;
1965 if (!movement_occurred && _stop) {
1969 motion (event, false);
1971 if (_item == &_editor->playhead_cursor->canvas_item) {
1972 Session* s = _editor->session ();
1974 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
1975 _editor->_pending_locate_request = true;
1976 s->request_resume_timecode_transmission ();
1982 CursorDrag::aborted ()
1984 if (_editor->_dragging_playhead) {
1985 _editor->session()->request_resume_timecode_transmission ();
1986 _editor->_dragging_playhead = false;
1989 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
1992 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1993 : RegionDrag (e, i, p, v)
1995 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
1999 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2001 Drag::start_grab (event, cursor);
2003 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2004 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2006 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2007 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2009 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2013 FadeInDrag::motion (GdkEvent* event, bool)
2015 framecnt_t fade_length;
2017 framepos_t const pos = adjusted_current_frame (event);
2019 boost::shared_ptr<Region> region = _primary->region ();
2021 if (pos < (region->position() + 64)) {
2022 fade_length = 64; // this should be a minimum defined somewhere
2023 } else if (pos > region->last_frame()) {
2024 fade_length = region->length();
2026 fade_length = pos - region->position();
2029 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2031 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2037 tmp->reset_fade_in_shape_width (fade_length);
2038 tmp->show_fade_line((framecnt_t) fade_length);
2041 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2045 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2047 if (!movement_occurred) {
2051 framecnt_t fade_length;
2053 framepos_t const pos = adjusted_current_frame (event);
2055 boost::shared_ptr<Region> region = _primary->region ();
2057 if (pos < (region->position() + 64)) {
2058 fade_length = 64; // this should be a minimum defined somewhere
2059 } else if (pos > region->last_frame()) {
2060 fade_length = region->length();
2062 fade_length = pos - region->position();
2065 _editor->begin_reversible_command (_("change fade in length"));
2067 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2069 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2075 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2076 XMLNode &before = alist->get_state();
2078 tmp->audio_region()->set_fade_in_length (fade_length);
2079 tmp->audio_region()->set_fade_in_active (true);
2080 tmp->hide_fade_line();
2082 XMLNode &after = alist->get_state();
2083 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2086 _editor->commit_reversible_command ();
2090 FadeInDrag::aborted ()
2092 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2093 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2099 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2100 tmp->hide_fade_line();
2104 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2105 : RegionDrag (e, i, p, v)
2107 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2111 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2113 Drag::start_grab (event, cursor);
2115 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2116 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2118 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2119 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2121 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2125 FadeOutDrag::motion (GdkEvent* event, bool)
2127 framecnt_t fade_length;
2129 framepos_t const pos = adjusted_current_frame (event);
2131 boost::shared_ptr<Region> region = _primary->region ();
2133 if (pos > (region->last_frame() - 64)) {
2134 fade_length = 64; // this should really be a minimum fade defined somewhere
2136 else if (pos < region->position()) {
2137 fade_length = region->length();
2140 fade_length = region->last_frame() - pos;
2143 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2145 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2151 tmp->reset_fade_out_shape_width (fade_length);
2152 tmp->show_fade_line(region->length() - fade_length);
2155 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2159 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2161 if (!movement_occurred) {
2165 framecnt_t fade_length;
2167 framepos_t const pos = adjusted_current_frame (event);
2169 boost::shared_ptr<Region> region = _primary->region ();
2171 if (pos > (region->last_frame() - 64)) {
2172 fade_length = 64; // this should really be a minimum fade defined somewhere
2174 else if (pos < region->position()) {
2175 fade_length = region->length();
2178 fade_length = region->last_frame() - pos;
2181 _editor->begin_reversible_command (_("change fade out length"));
2183 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2185 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2191 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2192 XMLNode &before = alist->get_state();
2194 tmp->audio_region()->set_fade_out_length (fade_length);
2195 tmp->audio_region()->set_fade_out_active (true);
2196 tmp->hide_fade_line();
2198 XMLNode &after = alist->get_state();
2199 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2202 _editor->commit_reversible_command ();
2206 FadeOutDrag::aborted ()
2208 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2209 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2215 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2216 tmp->hide_fade_line();
2220 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2223 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2225 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2228 _points.push_back (Gnome::Art::Point (0, 0));
2229 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2231 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2232 _line->property_width_pixels() = 1;
2233 _line->property_points () = _points;
2236 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2239 MarkerDrag::~MarkerDrag ()
2241 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2247 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2249 Drag::start_grab (event, cursor);
2253 Location *location = _editor->find_location_from_marker (_marker, is_start);
2254 _editor->_dragging_edit_point = true;
2256 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2258 update_item (location);
2260 // _drag_line->show();
2261 // _line->raise_to_top();
2264 _editor->show_verbose_time_cursor (location->start(), 10);
2266 _editor->show_verbose_time_cursor (location->end(), 10);
2269 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2272 case Selection::Toggle:
2273 _editor->selection->toggle (_marker);
2275 case Selection::Set:
2276 if (!_editor->selection->selected (_marker)) {
2277 _editor->selection->set (_marker);
2280 case Selection::Extend:
2282 Locations::LocationList ll;
2283 list<Marker*> to_add;
2285 _editor->selection->markers.range (s, e);
2286 s = min (_marker->position(), s);
2287 e = max (_marker->position(), e);
2290 if (e < max_frames) {
2293 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2294 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2295 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2298 to_add.push_back (lm->start);
2301 to_add.push_back (lm->end);
2305 if (!to_add.empty()) {
2306 _editor->selection->add (to_add);
2310 case Selection::Add:
2311 _editor->selection->add (_marker);
2315 /* Set up copies for us to manipulate during the drag */
2317 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2318 Location* l = _editor->find_location_from_marker (*i, is_start);
2319 _copied_locations.push_back (new Location (*l));
2324 MarkerDrag::motion (GdkEvent* event, bool)
2326 framecnt_t f_delta = 0;
2328 bool move_both = false;
2330 Location *real_location;
2331 Location *copy_location = 0;
2333 framepos_t const newframe = adjusted_current_frame (event);
2335 framepos_t next = newframe;
2337 if (newframe == last_pointer_frame()) {
2341 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2345 MarkerSelection::iterator i;
2346 list<Location*>::iterator x;
2348 /* find the marker we're dragging, and compute the delta */
2350 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2351 x != _copied_locations.end() && i != _editor->selection->markers.end();
2357 if (marker == _marker) {
2359 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2364 if (real_location->is_mark()) {
2365 f_delta = newframe - copy_location->start();
2369 switch (marker->type()) {
2371 case Marker::LoopStart:
2372 case Marker::PunchIn:
2373 f_delta = newframe - copy_location->start();
2377 case Marker::LoopEnd:
2378 case Marker::PunchOut:
2379 f_delta = newframe - copy_location->end();
2382 /* what kind of marker is this ? */
2390 if (i == _editor->selection->markers.end()) {
2391 /* hmm, impossible - we didn't find the dragged marker */
2395 /* now move them all */
2397 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2398 x != _copied_locations.end() && i != _editor->selection->markers.end();
2404 /* call this to find out if its the start or end */
2406 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2410 if (real_location->locked()) {
2414 if (copy_location->is_mark()) {
2418 copy_location->set_start (copy_location->start() + f_delta);
2422 framepos_t new_start = copy_location->start() + f_delta;
2423 framepos_t new_end = copy_location->end() + f_delta;
2425 if (is_start) { // start-of-range marker
2428 copy_location->set_start (new_start);
2429 copy_location->set_end (new_end);
2430 } else if (new_start < copy_location->end()) {
2431 copy_location->set_start (new_start);
2433 _editor->snap_to (next, 1, true);
2434 copy_location->set_end (next);
2435 copy_location->set_start (newframe);
2438 } else { // end marker
2441 copy_location->set_end (new_end);
2442 copy_location->set_start (new_start);
2443 } else if (new_end > copy_location->start()) {
2444 copy_location->set_end (new_end);
2445 } else if (newframe > 0) {
2446 _editor->snap_to (next, -1, true);
2447 copy_location->set_start (next);
2448 copy_location->set_end (newframe);
2453 update_item (copy_location);
2455 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2458 lm->set_position (copy_location->start(), copy_location->end());
2462 assert (!_copied_locations.empty());
2464 _editor->show_verbose_time_cursor (newframe, 10);
2467 _editor->update_canvas_now ();
2472 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2474 if (!movement_occurred) {
2476 /* just a click, do nothing but finish
2477 off the selection process
2480 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2483 case Selection::Set:
2484 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2485 _editor->selection->set (_marker);
2489 case Selection::Toggle:
2490 case Selection::Extend:
2491 case Selection::Add:
2498 _editor->_dragging_edit_point = false;
2500 _editor->begin_reversible_command ( _("move marker") );
2501 XMLNode &before = _editor->session()->locations()->get_state();
2503 MarkerSelection::iterator i;
2504 list<Location*>::iterator x;
2507 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2508 x != _copied_locations.end() && i != _editor->selection->markers.end();
2511 Location * location = _editor->find_location_from_marker (*i, is_start);
2515 if (location->locked()) {
2519 if (location->is_mark()) {
2520 location->set_start ((*x)->start());
2522 location->set ((*x)->start(), (*x)->end());
2527 XMLNode &after = _editor->session()->locations()->get_state();
2528 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2529 _editor->commit_reversible_command ();
2535 MarkerDrag::aborted ()
2541 MarkerDrag::update_item (Location* location)
2543 double const x1 = _editor->frame_to_pixel (location->start());
2545 _points.front().set_x(x1);
2546 _points.back().set_x(x1);
2547 _line->property_points() = _points;
2550 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2552 _cumulative_x_drag (0),
2553 _cumulative_y_drag (0)
2555 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2557 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2563 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2565 Drag::start_grab (event, _editor->fader_cursor);
2567 // start the grab at the center of the control point so
2568 // the point doesn't 'jump' to the mouse after the first drag
2569 _fixed_grab_x = _point->get_x();
2570 _fixed_grab_y = _point->get_y();
2572 float const fraction = 1 - (_point->get_y() / _point->line().height());
2574 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2576 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2577 event->button.x + 10, event->button.y + 10);
2579 _editor->show_verbose_canvas_cursor ();
2583 ControlPointDrag::motion (GdkEvent* event, bool)
2585 double dx = _drags->current_pointer_x() - last_pointer_x();
2586 double dy = _drags->current_pointer_y() - last_pointer_y();
2588 if (event->button.state & Keyboard::SecondaryModifier) {
2593 /* coordinate in pixels relative to the start of the region (for region-based automation)
2594 or track (for track-based automation) */
2595 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2596 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2598 // calculate zero crossing point. back off by .01 to stay on the
2599 // positive side of zero
2600 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2602 // make sure we hit zero when passing through
2603 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2607 if (_x_constrained) {
2610 if (_y_constrained) {
2614 _cumulative_x_drag = cx - _fixed_grab_x;
2615 _cumulative_y_drag = cy - _fixed_grab_y;
2619 cy = min ((double) _point->line().height(), cy);
2621 framepos_t cx_frames = _editor->unit_to_frame (cx);
2623 if (!_x_constrained) {
2624 _editor->snap_to_with_modifier (cx_frames, event);
2627 cx_frames = min (cx_frames, _point->line().maximum_time());
2629 float const fraction = 1.0 - (cy / _point->line().height());
2631 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2633 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2635 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2639 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2641 if (!movement_occurred) {
2645 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2646 _editor->reset_point_selection ();
2650 motion (event, false);
2652 _point->line().end_drag ();
2656 ControlPointDrag::aborted ()
2658 _point->line().reset ();
2662 ControlPointDrag::active (Editing::MouseMode m)
2664 if (m == Editing::MouseGain) {
2665 /* always active in mouse gain */
2669 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2670 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2673 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2676 _cumulative_y_drag (0)
2678 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2682 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2684 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2687 _item = &_line->grab_item ();
2689 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2690 origin, and ditto for y.
2693 double cx = event->button.x;
2694 double cy = event->button.y;
2696 _line->parent_group().w2i (cx, cy);
2698 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2703 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2704 /* no adjacent points */
2708 Drag::start_grab (event, _editor->fader_cursor);
2710 /* store grab start in parent frame */
2715 double fraction = 1.0 - (cy / _line->height());
2717 _line->start_drag_line (before, after, fraction);
2719 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2720 event->button.x + 10, event->button.y + 10);
2722 _editor->show_verbose_canvas_cursor ();
2726 LineDrag::motion (GdkEvent* event, bool)
2728 double dy = _drags->current_pointer_y() - last_pointer_y();
2730 if (event->button.state & Keyboard::SecondaryModifier) {
2734 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2736 _cumulative_y_drag = cy - _fixed_grab_y;
2739 cy = min ((double) _line->height(), cy);
2741 double const fraction = 1.0 - (cy / _line->height());
2745 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2751 /* we are ignoring x position for this drag, so we can just pass in anything */
2752 _line->drag_motion (0, fraction, true, push);
2754 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2758 LineDrag::finished (GdkEvent* event, bool)
2760 motion (event, false);
2765 LineDrag::aborted ()
2770 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2773 _cumulative_x_drag (0)
2775 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2779 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2781 Drag::start_grab (event);
2783 _line = reinterpret_cast<SimpleLine*> (_item);
2786 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2788 double cx = event->button.x;
2789 double cy = event->button.y;
2791 _item->property_parent().get_value()->w2i(cx, cy);
2793 /* store grab start in parent frame */
2794 _region_view_grab_x = cx;
2796 _before = _line->property_x1();
2798 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2800 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2804 FeatureLineDrag::motion (GdkEvent* event, bool)
2806 double dx = _drags->current_pointer_x() - last_pointer_x();
2808 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2810 _cumulative_x_drag += dx;
2812 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2821 _line->property_x1() = cx;
2822 _line->property_x2() = cx;
2824 _before = _line->property_x1();
2828 FeatureLineDrag::finished (GdkEvent* event, bool)
2830 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2831 _arv->update_transient(_before, _line->property_x1());
2835 FeatureLineDrag::aborted ()
2840 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2843 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2847 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2849 Drag::start_grab (event);
2850 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2854 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2861 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2863 framepos_t grab = grab_frame ();
2864 if (Config->get_rubberbanding_snaps_to_grid ()) {
2865 _editor->snap_to_with_modifier (grab, event);
2868 /* base start and end on initial click position */
2878 if (_drags->current_pointer_y() < grab_y()) {
2879 y1 = _drags->current_pointer_y();
2882 y2 = _drags->current_pointer_y();
2887 if (start != end || y1 != y2) {
2889 double x1 = _editor->frame_to_pixel (start);
2890 double x2 = _editor->frame_to_pixel (end);
2892 _editor->rubberband_rect->property_x1() = x1;
2893 _editor->rubberband_rect->property_y1() = y1;
2894 _editor->rubberband_rect->property_x2() = x2;
2895 _editor->rubberband_rect->property_y2() = y2;
2897 _editor->rubberband_rect->show();
2898 _editor->rubberband_rect->raise_to_top();
2900 _editor->show_verbose_time_cursor (pf, 10);
2905 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2907 if (movement_occurred) {
2909 motion (event, false);
2912 if (_drags->current_pointer_y() < grab_y()) {
2913 y1 = _drags->current_pointer_y();
2916 y2 = _drags->current_pointer_y();
2921 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2924 _editor->begin_reversible_command (_("rubberband selection"));
2926 if (grab_frame() < last_pointer_frame()) {
2927 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
2929 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
2933 _editor->commit_reversible_command ();
2937 if (!getenv("ARDOUR_SAE")) {
2938 _editor->selection->clear_tracks();
2940 _editor->selection->clear_regions();
2941 _editor->selection->clear_points ();
2942 _editor->selection->clear_lines ();
2945 _editor->rubberband_rect->hide();
2949 RubberbandSelectDrag::aborted ()
2951 _editor->rubberband_rect->hide ();
2954 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
2955 : RegionDrag (e, i, p, v)
2957 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
2961 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2963 Drag::start_grab (event, cursor);
2965 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2969 TimeFXDrag::motion (GdkEvent* event, bool)
2971 RegionView* rv = _primary;
2973 framepos_t const pf = adjusted_current_frame (event);
2975 if (pf > rv->region()->position()) {
2976 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
2979 _editor->show_verbose_time_cursor (pf, 10);
2983 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2985 _primary->get_time_axis_view().hide_timestretch ();
2987 if (!movement_occurred) {
2991 if (last_pointer_frame() < _primary->region()->position()) {
2992 /* backwards drag of the left edge - not usable */
2996 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
2998 float percentage = (double) newlen / (double) _primary->region()->length();
3000 #ifndef USE_RUBBERBAND
3001 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3002 if (_primary->region()->data_type() == DataType::AUDIO) {
3003 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3007 _editor->begin_reversible_command (_("timestretch"));
3009 // XXX how do timeFX on multiple regions ?
3014 if (_editor->time_stretch (rs, percentage) == -1) {
3015 error << _("An error occurred while executing time stretch operation") << endmsg;
3020 TimeFXDrag::aborted ()
3022 _primary->get_time_axis_view().hide_timestretch ();
3025 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3028 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3032 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3034 Drag::start_grab (event);
3038 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3040 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3044 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3046 if (movement_occurred && _editor->session()) {
3047 /* make sure we stop */
3048 _editor->session()->request_transport_speed (0.0);
3053 ScrubDrag::aborted ()
3058 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3062 , _original_pointer_time_axis (-1)
3063 , _last_pointer_time_axis (-1)
3065 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3069 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3071 framepos_t start = 0;
3074 if (_editor->session() == 0) {
3078 Gdk::Cursor* cursor = 0;
3080 switch (_operation) {
3081 case CreateSelection:
3082 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3087 cursor = _editor->selector_cursor;
3088 Drag::start_grab (event, cursor);
3091 case SelectionStartTrim:
3092 if (_editor->clicked_axisview) {
3093 _editor->clicked_axisview->order_selection_trims (_item, true);
3095 Drag::start_grab (event, _editor->left_side_trim_cursor);
3096 start = _editor->selection->time[_editor->clicked_selection].start;
3097 _pointer_frame_offset = raw_grab_frame() - start;
3100 case SelectionEndTrim:
3101 if (_editor->clicked_axisview) {
3102 _editor->clicked_axisview->order_selection_trims (_item, false);
3104 Drag::start_grab (event, _editor->right_side_trim_cursor);
3105 end = _editor->selection->time[_editor->clicked_selection].end;
3106 _pointer_frame_offset = raw_grab_frame() - end;
3110 start = _editor->selection->time[_editor->clicked_selection].start;
3111 Drag::start_grab (event, cursor);
3112 _pointer_frame_offset = raw_grab_frame() - start;
3116 if (_operation == SelectionMove) {
3117 _editor->show_verbose_time_cursor (start, 10);
3119 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3122 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3126 SelectionDrag::motion (GdkEvent* event, bool first_move)
3128 framepos_t start = 0;
3132 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3133 if (pending_time_axis.first == 0) {
3137 framepos_t const pending_position = adjusted_current_frame (event);
3139 /* only alter selection if things have changed */
3141 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3145 switch (_operation) {
3146 case CreateSelection:
3148 framepos_t grab = grab_frame ();
3151 _editor->snap_to (grab);
3154 if (pending_position < grab_frame()) {
3155 start = pending_position;
3158 end = pending_position;
3162 /* first drag: Either add to the selection
3163 or create a new selection
3169 /* adding to the selection */
3170 _editor->set_selected_track_as_side_effect (Selection::Add);
3171 //_editor->selection->add (_editor->clicked_axisview);
3172 _editor->clicked_selection = _editor->selection->add (start, end);
3177 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3178 //_editor->selection->set (_editor->clicked_axisview);
3179 _editor->set_selected_track_as_side_effect (Selection::Set);
3182 _editor->clicked_selection = _editor->selection->set (start, end);
3186 /* select the track that we're in */
3187 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3188 // _editor->set_selected_track_as_side_effect (Selection::Add);
3189 _editor->selection->add (pending_time_axis.first);
3190 _added_time_axes.push_back (pending_time_axis.first);
3193 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3194 tracks that we selected in the first place.
3197 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3198 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3200 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3201 while (i != _added_time_axes.end()) {
3203 list<TimeAxisView*>::iterator tmp = i;
3206 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3207 _editor->selection->remove (*i);
3208 _added_time_axes.remove (*i);
3217 case SelectionStartTrim:
3219 start = _editor->selection->time[_editor->clicked_selection].start;
3220 end = _editor->selection->time[_editor->clicked_selection].end;
3222 if (pending_position > end) {
3225 start = pending_position;
3229 case SelectionEndTrim:
3231 start = _editor->selection->time[_editor->clicked_selection].start;
3232 end = _editor->selection->time[_editor->clicked_selection].end;
3234 if (pending_position < start) {
3237 end = pending_position;
3244 start = _editor->selection->time[_editor->clicked_selection].start;
3245 end = _editor->selection->time[_editor->clicked_selection].end;
3247 length = end - start;
3249 start = pending_position;
3250 _editor->snap_to (start);
3252 end = start + length;
3257 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3258 _editor->start_canvas_autoscroll (1, 0);
3262 _editor->selection->replace (_editor->clicked_selection, start, end);
3265 if (_operation == SelectionMove) {
3266 _editor->show_verbose_time_cursor(start, 10);
3268 _editor->show_verbose_time_cursor(pending_position, 10);
3273 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3275 Session* s = _editor->session();
3277 if (movement_occurred) {
3278 motion (event, false);
3279 /* XXX this is not object-oriented programming at all. ick */
3280 if (_editor->selection->time.consolidate()) {
3281 _editor->selection->TimeChanged ();
3284 /* XXX what if its a music time selection? */
3285 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3286 s->request_play_range (&_editor->selection->time, true);
3291 /* just a click, no pointer movement.*/
3293 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3294 _editor->selection->clear_time();
3297 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3298 _editor->selection->set (_editor->clicked_axisview);
3301 if (s && s->get_play_range () && s->transport_rolling()) {
3302 s->request_stop (false, false);
3307 _editor->stop_canvas_autoscroll ();
3311 SelectionDrag::aborted ()
3316 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3321 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3323 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3324 physical_screen_height (_editor->get_window()));
3325 _drag_rect->hide ();
3327 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3328 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3332 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3334 if (_editor->session() == 0) {
3338 Gdk::Cursor* cursor = 0;
3340 if (!_editor->temp_location) {
3341 _editor->temp_location = new Location (*_editor->session());
3344 switch (_operation) {
3345 case CreateRangeMarker:
3346 case CreateTransportMarker:
3347 case CreateCDMarker:
3349 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3354 cursor = _editor->selector_cursor;
3358 Drag::start_grab (event, cursor);
3360 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3364 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3366 framepos_t start = 0;
3368 ArdourCanvas::SimpleRect *crect;
3370 switch (_operation) {
3371 case CreateRangeMarker:
3372 crect = _editor->range_bar_drag_rect;
3374 case CreateTransportMarker:
3375 crect = _editor->transport_bar_drag_rect;
3377 case CreateCDMarker:
3378 crect = _editor->cd_marker_bar_drag_rect;
3381 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3386 framepos_t const pf = adjusted_current_frame (event);
3388 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3389 framepos_t grab = grab_frame ();
3390 _editor->snap_to (grab);
3392 if (pf < grab_frame()) {
3400 /* first drag: Either add to the selection
3401 or create a new selection.
3406 _editor->temp_location->set (start, end);
3410 update_item (_editor->temp_location);
3412 //_drag_rect->raise_to_top();
3417 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3418 _editor->start_canvas_autoscroll (1, 0);
3422 _editor->temp_location->set (start, end);
3424 double x1 = _editor->frame_to_pixel (start);
3425 double x2 = _editor->frame_to_pixel (end);
3426 crect->property_x1() = x1;
3427 crect->property_x2() = x2;
3429 update_item (_editor->temp_location);
3432 _editor->show_verbose_time_cursor (pf, 10);
3437 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3439 Location * newloc = 0;
3443 if (movement_occurred) {
3444 motion (event, false);
3447 switch (_operation) {
3448 case CreateRangeMarker:
3449 case CreateCDMarker:
3451 _editor->begin_reversible_command (_("new range marker"));
3452 XMLNode &before = _editor->session()->locations()->get_state();
3453 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3454 if (_operation == CreateCDMarker) {
3455 flags = Location::IsRangeMarker | Location::IsCDMarker;
3456 _editor->cd_marker_bar_drag_rect->hide();
3459 flags = Location::IsRangeMarker;
3460 _editor->range_bar_drag_rect->hide();
3462 newloc = new Location (
3463 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3466 _editor->session()->locations()->add (newloc, true);
3467 XMLNode &after = _editor->session()->locations()->get_state();
3468 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3469 _editor->commit_reversible_command ();
3473 case CreateTransportMarker:
3474 // popup menu to pick loop or punch
3475 _editor->new_transport_marker_context_menu (&event->button, _item);
3479 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3481 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3486 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3488 if (end == max_frames) {
3489 end = _editor->session()->current_end_frame ();
3492 if (start == max_frames) {
3493 start = _editor->session()->current_start_frame ();
3496 switch (_editor->mouse_mode) {
3498 /* find the two markers on either side and then make the selection from it */
3499 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3503 /* find the two markers on either side of the click and make the range out of it */
3504 _editor->selection->set (start, end);
3513 _editor->stop_canvas_autoscroll ();
3517 RangeMarkerBarDrag::aborted ()
3523 RangeMarkerBarDrag::update_item (Location* location)
3525 double const x1 = _editor->frame_to_pixel (location->start());
3526 double const x2 = _editor->frame_to_pixel (location->end());
3528 _drag_rect->property_x1() = x1;
3529 _drag_rect->property_x2() = x2;
3532 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3535 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3539 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3541 Drag::start_grab (event, _editor->zoom_cursor);
3542 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3546 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3551 framepos_t const pf = adjusted_current_frame (event);
3553 framepos_t grab = grab_frame ();
3554 _editor->snap_to_with_modifier (grab, event);
3556 /* base start and end on initial click position */
3568 _editor->zoom_rect->show();
3569 _editor->zoom_rect->raise_to_top();
3572 _editor->reposition_zoom_rect(start, end);
3574 _editor->show_verbose_time_cursor (pf, 10);
3579 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3581 if (movement_occurred) {
3582 motion (event, false);
3584 if (grab_frame() < last_pointer_frame()) {
3585 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3587 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3590 _editor->temporal_zoom_to_frame (false, grab_frame());
3592 temporal_zoom_step (false);
3593 center_screen (grab_frame());
3597 _editor->zoom_rect->hide();
3601 MouseZoomDrag::aborted ()
3603 _editor->zoom_rect->hide ();
3606 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3608 , _cumulative_dx (0)
3609 , _cumulative_dy (0)
3611 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3613 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3614 _region = &_primary->region_view ();
3615 _note_height = _region->midi_stream_view()->note_height ();
3619 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3621 Drag::start_grab (event);
3623 if (!(_was_selected = _primary->selected())) {
3625 /* tertiary-click means extend selection - we'll do that on button release,
3626 so don't add it here, because otherwise we make it hard to figure
3627 out the "extend-to" range.
3630 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3633 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3636 _region->note_selected (_primary, true);
3638 _region->unique_select (_primary);
3644 /** @return Current total drag x change in frames */
3646 NoteDrag::total_dx () const
3649 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3651 /* primary note time */
3652 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3654 /* new time of the primary note relative to the region position */
3655 frameoffset_t const st = n + dx;
3657 /* snap and return corresponding delta */
3658 return _region->snap_frame_to_frame (st) - n;
3661 /** @return Current total drag y change in notes */
3663 NoteDrag::total_dy () const
3665 /* this is `backwards' to make increasing note number go in the right direction */
3666 double const dy = _drags->current_pointer_y() - grab_y();
3671 if (abs (dy) >= _note_height) {
3673 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3675 ndy = (int8_t) floor (dy / _note_height / 2.0);
3684 NoteDrag::motion (GdkEvent *, bool)
3686 /* Total change in x and y since the start of the drag */
3687 frameoffset_t const dx = total_dx ();
3688 int8_t const dy = total_dy ();
3690 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3691 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3692 double const tdy = dy * _note_height - _cumulative_dy;
3695 _region->move_selection (tdx, tdy);
3696 _cumulative_dx += tdx;
3697 _cumulative_dy += tdy;
3700 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + dy).c_str(),
3701 (int) floor (_primary->note()->note() + dy));
3703 _editor->show_verbose_canvas_cursor_with (buf);
3708 NoteDrag::finished (GdkEvent* ev, bool moved)
3711 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3713 if (_was_selected) {
3714 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3716 _region->note_deselected (_primary);
3719 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3720 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3722 if (!extend && !add && _region->selection_size() > 1) {
3723 _region->unique_select (_primary);
3724 } else if (extend) {
3725 _region->note_selected (_primary, true, true);
3727 /* it was added during button press */
3732 _region->note_dropped (_primary, total_dx(), - total_dy());
3737 NoteDrag::aborted ()
3742 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3745 , _nothing_to_drag (false)
3747 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3749 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3752 _line = _atav->line ();
3756 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3758 Drag::start_grab (event, cursor);
3760 list<ControlPoint*> points;
3762 XMLNode* state = &_line->get_state ();
3764 if (_ranges.empty()) {
3766 uint32_t const N = _line->npoints ();
3767 for (uint32_t i = 0; i < N; ++i) {
3768 points.push_back (_line->nth (i));
3773 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3774 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3776 /* fade into and out of the region that we're dragging;
3777 64 samples length plucked out of thin air.
3779 framecnt_t const h = (j->start + j->end) / 2;
3780 framepos_t a = j->start + 64;
3784 framepos_t b = j->end - 64;
3789 the_list->add (j->start, the_list->eval (j->start));
3790 _line->add_always_in_view (j->start);
3791 the_list->add (a, the_list->eval (a));
3792 _line->add_always_in_view (a);
3793 the_list->add (b, the_list->eval (b));
3794 _line->add_always_in_view (b);
3795 the_list->add (j->end, the_list->eval (j->end));
3796 _line->add_always_in_view (j->end);
3799 uint32_t const N = _line->npoints ();
3800 for (uint32_t i = 0; i < N; ++i) {
3802 ControlPoint* p = _line->nth (i);
3804 list<AudioRange>::const_iterator j = _ranges.begin ();
3805 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3809 if (j != _ranges.end()) {
3810 points.push_back (p);
3815 if (points.empty()) {
3816 _nothing_to_drag = true;
3820 _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
3824 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3826 if (_nothing_to_drag) {
3830 float const f = 1 - (_drags->current_pointer_y() / _line->height());
3832 /* we are ignoring x position for this drag, so we can just pass in anything */
3833 _line->drag_motion (0, f, true, false);
3837 AutomationRangeDrag::finished (GdkEvent* event, bool)
3839 if (_nothing_to_drag) {
3843 motion (event, false);
3845 _line->clear_always_in_view ();
3849 AutomationRangeDrag::aborted ()
3851 _line->clear_always_in_view ();
3855 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
3858 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
3859 layer = v->region()->layer ();
3860 initial_y = v->get_canvas_group()->property_y ();
3861 initial_playlist = v->region()->playlist ();