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);
2653 _point->line().end_drag ();
2654 _editor->session()->commit_reversible_command ();
2658 ControlPointDrag::aborted ()
2660 _point->line().reset ();
2664 ControlPointDrag::active (Editing::MouseMode m)
2666 if (m == Editing::MouseGain) {
2667 /* always active in mouse gain */
2671 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2672 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2675 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2678 _cumulative_y_drag (0)
2680 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2684 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2686 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2689 _item = &_line->grab_item ();
2691 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2692 origin, and ditto for y.
2695 double cx = event->button.x;
2696 double cy = event->button.y;
2698 _line->parent_group().w2i (cx, cy);
2700 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2705 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2706 /* no adjacent points */
2710 Drag::start_grab (event, _editor->fader_cursor);
2712 /* store grab start in parent frame */
2717 double fraction = 1.0 - (cy / _line->height());
2719 _line->start_drag_line (before, after, fraction);
2721 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2722 event->button.x + 10, event->button.y + 10);
2724 _editor->show_verbose_canvas_cursor ();
2728 LineDrag::motion (GdkEvent* event, bool)
2730 double dy = _drags->current_pointer_y() - last_pointer_y();
2732 if (event->button.state & Keyboard::SecondaryModifier) {
2736 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2738 _cumulative_y_drag = cy - _fixed_grab_y;
2741 cy = min ((double) _line->height(), cy);
2743 double const fraction = 1.0 - (cy / _line->height());
2747 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2753 /* we are ignoring x position for this drag, so we can just pass in anything */
2754 _line->drag_motion (0, fraction, true, push);
2756 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2760 LineDrag::finished (GdkEvent* event, bool)
2762 motion (event, false);
2764 _editor->session()->commit_reversible_command ();
2768 LineDrag::aborted ()
2773 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2776 _cumulative_x_drag (0)
2778 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2782 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2784 Drag::start_grab (event);
2786 _line = reinterpret_cast<SimpleLine*> (_item);
2789 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2791 double cx = event->button.x;
2792 double cy = event->button.y;
2794 _item->property_parent().get_value()->w2i(cx, cy);
2796 /* store grab start in parent frame */
2797 _region_view_grab_x = cx;
2799 _before = _line->property_x1();
2801 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2803 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2807 FeatureLineDrag::motion (GdkEvent* event, bool)
2809 double dx = _drags->current_pointer_x() - last_pointer_x();
2811 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2813 _cumulative_x_drag += dx;
2815 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2824 _line->property_x1() = cx;
2825 _line->property_x2() = cx;
2827 _before = _line->property_x1();
2831 FeatureLineDrag::finished (GdkEvent* event, bool)
2833 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2834 _arv->update_transient(_before, _line->property_x1());
2838 FeatureLineDrag::aborted ()
2843 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2846 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2850 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2852 Drag::start_grab (event);
2853 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2857 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2864 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2866 framepos_t grab = grab_frame ();
2867 if (Config->get_rubberbanding_snaps_to_grid ()) {
2868 _editor->snap_to_with_modifier (grab, event);
2871 /* base start and end on initial click position */
2881 if (_drags->current_pointer_y() < grab_y()) {
2882 y1 = _drags->current_pointer_y();
2885 y2 = _drags->current_pointer_y();
2890 if (start != end || y1 != y2) {
2892 double x1 = _editor->frame_to_pixel (start);
2893 double x2 = _editor->frame_to_pixel (end);
2895 _editor->rubberband_rect->property_x1() = x1;
2896 _editor->rubberband_rect->property_y1() = y1;
2897 _editor->rubberband_rect->property_x2() = x2;
2898 _editor->rubberband_rect->property_y2() = y2;
2900 _editor->rubberband_rect->show();
2901 _editor->rubberband_rect->raise_to_top();
2903 _editor->show_verbose_time_cursor (pf, 10);
2908 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2910 if (movement_occurred) {
2912 motion (event, false);
2915 if (_drags->current_pointer_y() < grab_y()) {
2916 y1 = _drags->current_pointer_y();
2919 y2 = _drags->current_pointer_y();
2924 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2927 _editor->begin_reversible_command (_("rubberband selection"));
2929 if (grab_frame() < last_pointer_frame()) {
2930 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
2932 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
2936 _editor->commit_reversible_command ();
2940 if (!getenv("ARDOUR_SAE")) {
2941 _editor->selection->clear_tracks();
2943 _editor->selection->clear_regions();
2944 _editor->selection->clear_points ();
2945 _editor->selection->clear_lines ();
2948 _editor->rubberband_rect->hide();
2952 RubberbandSelectDrag::aborted ()
2954 _editor->rubberband_rect->hide ();
2957 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
2958 : RegionDrag (e, i, p, v)
2960 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
2964 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2966 Drag::start_grab (event, cursor);
2968 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2972 TimeFXDrag::motion (GdkEvent* event, bool)
2974 RegionView* rv = _primary;
2976 framepos_t const pf = adjusted_current_frame (event);
2978 if (pf > rv->region()->position()) {
2979 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
2982 _editor->show_verbose_time_cursor (pf, 10);
2986 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2988 _primary->get_time_axis_view().hide_timestretch ();
2990 if (!movement_occurred) {
2994 if (last_pointer_frame() < _primary->region()->position()) {
2995 /* backwards drag of the left edge - not usable */
2999 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3001 float percentage = (double) newlen / (double) _primary->region()->length();
3003 #ifndef USE_RUBBERBAND
3004 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3005 if (_primary->region()->data_type() == DataType::AUDIO) {
3006 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3010 _editor->begin_reversible_command (_("timestretch"));
3012 // XXX how do timeFX on multiple regions ?
3017 if (_editor->time_stretch (rs, percentage) == -1) {
3018 error << _("An error occurred while executing time stretch operation") << endmsg;
3023 TimeFXDrag::aborted ()
3025 _primary->get_time_axis_view().hide_timestretch ();
3028 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3031 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3035 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3037 Drag::start_grab (event);
3041 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3043 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3047 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3049 if (movement_occurred && _editor->session()) {
3050 /* make sure we stop */
3051 _editor->session()->request_transport_speed (0.0);
3056 ScrubDrag::aborted ()
3061 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3065 , _original_pointer_time_axis (-1)
3066 , _last_pointer_time_axis (-1)
3068 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3072 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3074 framepos_t start = 0;
3077 if (_editor->session() == 0) {
3081 Gdk::Cursor* cursor = 0;
3083 switch (_operation) {
3084 case CreateSelection:
3085 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3090 cursor = _editor->selector_cursor;
3091 Drag::start_grab (event, cursor);
3094 case SelectionStartTrim:
3095 if (_editor->clicked_axisview) {
3096 _editor->clicked_axisview->order_selection_trims (_item, true);
3098 Drag::start_grab (event, _editor->left_side_trim_cursor);
3099 start = _editor->selection->time[_editor->clicked_selection].start;
3100 _pointer_frame_offset = raw_grab_frame() - start;
3103 case SelectionEndTrim:
3104 if (_editor->clicked_axisview) {
3105 _editor->clicked_axisview->order_selection_trims (_item, false);
3107 Drag::start_grab (event, _editor->right_side_trim_cursor);
3108 end = _editor->selection->time[_editor->clicked_selection].end;
3109 _pointer_frame_offset = raw_grab_frame() - end;
3113 start = _editor->selection->time[_editor->clicked_selection].start;
3114 Drag::start_grab (event, cursor);
3115 _pointer_frame_offset = raw_grab_frame() - start;
3119 if (_operation == SelectionMove) {
3120 _editor->show_verbose_time_cursor (start, 10);
3122 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3125 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3129 SelectionDrag::motion (GdkEvent* event, bool first_move)
3131 framepos_t start = 0;
3135 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3136 if (pending_time_axis.first == 0) {
3140 framepos_t const pending_position = adjusted_current_frame (event);
3142 /* only alter selection if things have changed */
3144 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3148 switch (_operation) {
3149 case CreateSelection:
3151 framepos_t grab = grab_frame ();
3154 _editor->snap_to (grab);
3157 if (pending_position < grab_frame()) {
3158 start = pending_position;
3161 end = pending_position;
3165 /* first drag: Either add to the selection
3166 or create a new selection
3172 /* adding to the selection */
3173 _editor->set_selected_track_as_side_effect (Selection::Add);
3174 //_editor->selection->add (_editor->clicked_axisview);
3175 _editor->clicked_selection = _editor->selection->add (start, end);
3180 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3181 //_editor->selection->set (_editor->clicked_axisview);
3182 _editor->set_selected_track_as_side_effect (Selection::Set);
3185 _editor->clicked_selection = _editor->selection->set (start, end);
3189 /* select the track that we're in */
3190 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3191 // _editor->set_selected_track_as_side_effect (Selection::Add);
3192 _editor->selection->add (pending_time_axis.first);
3193 _added_time_axes.push_back (pending_time_axis.first);
3196 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3197 tracks that we selected in the first place.
3200 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3201 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3203 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3204 while (i != _added_time_axes.end()) {
3206 list<TimeAxisView*>::iterator tmp = i;
3209 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3210 _editor->selection->remove (*i);
3211 _added_time_axes.remove (*i);
3220 case SelectionStartTrim:
3222 start = _editor->selection->time[_editor->clicked_selection].start;
3223 end = _editor->selection->time[_editor->clicked_selection].end;
3225 if (pending_position > end) {
3228 start = pending_position;
3232 case SelectionEndTrim:
3234 start = _editor->selection->time[_editor->clicked_selection].start;
3235 end = _editor->selection->time[_editor->clicked_selection].end;
3237 if (pending_position < start) {
3240 end = pending_position;
3247 start = _editor->selection->time[_editor->clicked_selection].start;
3248 end = _editor->selection->time[_editor->clicked_selection].end;
3250 length = end - start;
3252 start = pending_position;
3253 _editor->snap_to (start);
3255 end = start + length;
3260 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3261 _editor->start_canvas_autoscroll (1, 0);
3265 _editor->selection->replace (_editor->clicked_selection, start, end);
3268 if (_operation == SelectionMove) {
3269 _editor->show_verbose_time_cursor(start, 10);
3271 _editor->show_verbose_time_cursor(pending_position, 10);
3276 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3278 Session* s = _editor->session();
3280 if (movement_occurred) {
3281 motion (event, false);
3282 /* XXX this is not object-oriented programming at all. ick */
3283 if (_editor->selection->time.consolidate()) {
3284 _editor->selection->TimeChanged ();
3287 /* XXX what if its a music time selection? */
3288 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3289 s->request_play_range (&_editor->selection->time, true);
3294 /* just a click, no pointer movement.*/
3296 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3297 _editor->selection->clear_time();
3300 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3301 _editor->selection->set (_editor->clicked_axisview);
3304 if (s && s->get_play_range () && s->transport_rolling()) {
3305 s->request_stop (false, false);
3310 _editor->stop_canvas_autoscroll ();
3314 SelectionDrag::aborted ()
3319 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3324 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3326 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3327 physical_screen_height (_editor->get_window()));
3328 _drag_rect->hide ();
3330 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3331 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3335 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3337 if (_editor->session() == 0) {
3341 Gdk::Cursor* cursor = 0;
3343 if (!_editor->temp_location) {
3344 _editor->temp_location = new Location (*_editor->session());
3347 switch (_operation) {
3348 case CreateRangeMarker:
3349 case CreateTransportMarker:
3350 case CreateCDMarker:
3352 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3357 cursor = _editor->selector_cursor;
3361 Drag::start_grab (event, cursor);
3363 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3367 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3369 framepos_t start = 0;
3371 ArdourCanvas::SimpleRect *crect;
3373 switch (_operation) {
3374 case CreateRangeMarker:
3375 crect = _editor->range_bar_drag_rect;
3377 case CreateTransportMarker:
3378 crect = _editor->transport_bar_drag_rect;
3380 case CreateCDMarker:
3381 crect = _editor->cd_marker_bar_drag_rect;
3384 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3389 framepos_t const pf = adjusted_current_frame (event);
3391 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3392 framepos_t grab = grab_frame ();
3393 _editor->snap_to (grab);
3395 if (pf < grab_frame()) {
3403 /* first drag: Either add to the selection
3404 or create a new selection.
3409 _editor->temp_location->set (start, end);
3413 update_item (_editor->temp_location);
3415 //_drag_rect->raise_to_top();
3420 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3421 _editor->start_canvas_autoscroll (1, 0);
3425 _editor->temp_location->set (start, end);
3427 double x1 = _editor->frame_to_pixel (start);
3428 double x2 = _editor->frame_to_pixel (end);
3429 crect->property_x1() = x1;
3430 crect->property_x2() = x2;
3432 update_item (_editor->temp_location);
3435 _editor->show_verbose_time_cursor (pf, 10);
3440 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3442 Location * newloc = 0;
3446 if (movement_occurred) {
3447 motion (event, false);
3450 switch (_operation) {
3451 case CreateRangeMarker:
3452 case CreateCDMarker:
3454 _editor->begin_reversible_command (_("new range marker"));
3455 XMLNode &before = _editor->session()->locations()->get_state();
3456 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3457 if (_operation == CreateCDMarker) {
3458 flags = Location::IsRangeMarker | Location::IsCDMarker;
3459 _editor->cd_marker_bar_drag_rect->hide();
3462 flags = Location::IsRangeMarker;
3463 _editor->range_bar_drag_rect->hide();
3465 newloc = new Location (
3466 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3469 _editor->session()->locations()->add (newloc, true);
3470 XMLNode &after = _editor->session()->locations()->get_state();
3471 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3472 _editor->commit_reversible_command ();
3476 case CreateTransportMarker:
3477 // popup menu to pick loop or punch
3478 _editor->new_transport_marker_context_menu (&event->button, _item);
3482 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3484 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3489 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3491 if (end == max_frames) {
3492 end = _editor->session()->current_end_frame ();
3495 if (start == max_frames) {
3496 start = _editor->session()->current_start_frame ();
3499 switch (_editor->mouse_mode) {
3501 /* find the two markers on either side and then make the selection from it */
3502 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3506 /* find the two markers on either side of the click and make the range out of it */
3507 _editor->selection->set (start, end);
3516 _editor->stop_canvas_autoscroll ();
3520 RangeMarkerBarDrag::aborted ()
3526 RangeMarkerBarDrag::update_item (Location* location)
3528 double const x1 = _editor->frame_to_pixel (location->start());
3529 double const x2 = _editor->frame_to_pixel (location->end());
3531 _drag_rect->property_x1() = x1;
3532 _drag_rect->property_x2() = x2;
3535 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3538 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3542 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3544 Drag::start_grab (event, _editor->zoom_cursor);
3545 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3549 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3554 framepos_t const pf = adjusted_current_frame (event);
3556 framepos_t grab = grab_frame ();
3557 _editor->snap_to_with_modifier (grab, event);
3559 /* base start and end on initial click position */
3571 _editor->zoom_rect->show();
3572 _editor->zoom_rect->raise_to_top();
3575 _editor->reposition_zoom_rect(start, end);
3577 _editor->show_verbose_time_cursor (pf, 10);
3582 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3584 if (movement_occurred) {
3585 motion (event, false);
3587 if (grab_frame() < last_pointer_frame()) {
3588 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3590 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3593 _editor->temporal_zoom_to_frame (false, grab_frame());
3595 temporal_zoom_step (false);
3596 center_screen (grab_frame());
3600 _editor->zoom_rect->hide();
3604 MouseZoomDrag::aborted ()
3606 _editor->zoom_rect->hide ();
3609 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3611 , _cumulative_dx (0)
3612 , _cumulative_dy (0)
3614 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3616 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3617 _region = &_primary->region_view ();
3618 _note_height = _region->midi_stream_view()->note_height ();
3622 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3624 Drag::start_grab (event);
3626 if (!(_was_selected = _primary->selected())) {
3628 /* tertiary-click means extend selection - we'll do that on button release,
3629 so don't add it here, because otherwise we make it hard to figure
3630 out the "extend-to" range.
3633 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3636 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3639 _region->note_selected (_primary, true);
3641 _region->unique_select (_primary);
3647 /** @return Current total drag x change in frames */
3649 NoteDrag::total_dx () const
3652 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3654 /* primary note time */
3655 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3657 /* new time of the primary note relative to the region position */
3658 frameoffset_t const st = n + dx;
3660 /* snap and return corresponding delta */
3661 return _region->snap_frame_to_frame (st) - n;
3664 /** @return Current total drag y change in notes */
3666 NoteDrag::total_dy () const
3668 /* this is `backwards' to make increasing note number go in the right direction */
3669 double const dy = _drags->current_pointer_y() - grab_y();
3674 if (abs (dy) >= _note_height) {
3676 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3678 ndy = (int8_t) floor (dy / _note_height / 2.0);
3687 NoteDrag::motion (GdkEvent *, bool)
3689 /* Total change in x and y since the start of the drag */
3690 frameoffset_t const dx = total_dx ();
3691 int8_t const dy = total_dy ();
3693 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3694 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3695 double const tdy = dy * _note_height - _cumulative_dy;
3698 _region->move_selection (tdx, tdy);
3699 _cumulative_dx += tdx;
3700 _cumulative_dy += tdy;
3703 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + dy).c_str(),
3704 (int) floor (_primary->note()->note() + dy));
3706 _editor->show_verbose_canvas_cursor_with (buf);
3711 NoteDrag::finished (GdkEvent* ev, bool moved)
3714 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3716 if (_was_selected) {
3717 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3719 _region->note_deselected (_primary);
3722 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3723 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3725 if (!extend && !add && _region->selection_size() > 1) {
3726 _region->unique_select (_primary);
3727 } else if (extend) {
3728 _region->note_selected (_primary, true, true);
3730 /* it was added during button press */
3735 _region->note_dropped (_primary, total_dx(), - total_dy());
3740 NoteDrag::aborted ()
3745 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3746 : Drag (editor, item)
3748 , _nothing_to_drag (false)
3750 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3752 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3755 /* get all lines in the automation view */
3756 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3758 /* find those that overlap the ranges being dragged */
3759 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3760 while (i != lines.end ()) {
3761 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3764 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3766 /* check this range against all the AudioRanges that we are using */
3767 list<AudioRange>::const_iterator k = _ranges.begin ();
3768 while (k != _ranges.end()) {
3769 if (k->coverage (r.first, r.second) != OverlapNone) {
3775 /* add it to our list if it overlaps at all */
3776 if (k != _ranges.end()) {
3781 _lines.push_back (n);
3787 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3791 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3793 Drag::start_grab (event, cursor);
3795 /* Get line states before we start changing things */
3796 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3797 i->state = &i->line->get_state ();
3800 if (_ranges.empty()) {
3802 /* No selected time ranges: drag all points */
3803 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3804 uint32_t const N = i->line->npoints ();
3805 for (uint32_t j = 0; j < N; ++j) {
3806 i->points.push_back (i->line->nth (j));
3812 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3814 framecnt_t const half = (i->start + i->end) / 2;
3816 /* find the line that this audio range starts in */
3817 list<Line>::iterator j = _lines.begin();
3818 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3822 if (j != _lines.end()) {
3823 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3825 /* j is the line that this audio range starts in; fade into it;
3826 64 samples length plucked out of thin air.
3829 framepos_t a = i->start + 64;
3834 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3835 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3837 the_list->add (p, the_list->eval (p));
3838 j->line->add_always_in_view (p);
3839 the_list->add (q, the_list->eval (q));
3840 j->line->add_always_in_view (q);
3843 /* same thing for the end */
3846 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3850 if (j != _lines.end()) {
3851 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3853 /* j is the line that this audio range starts in; fade out of it;
3854 64 samples length plucked out of thin air.
3857 framepos_t b = i->end - 64;
3862 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3863 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
3865 the_list->add (p, the_list->eval (p));
3866 j->line->add_always_in_view (p);
3867 the_list->add (q, the_list->eval (q));
3868 j->line->add_always_in_view (q);
3872 _nothing_to_drag = true;
3874 /* Find all the points that should be dragged and put them in the relevant
3875 points lists in the Line structs.
3878 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3880 uint32_t const N = i->line->npoints ();
3881 for (uint32_t j = 0; j < N; ++j) {
3883 /* here's a control point on this line */
3884 ControlPoint* p = i->line->nth (j);
3885 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
3887 /* see if it's inside a range */
3888 list<AudioRange>::const_iterator k = _ranges.begin ();
3889 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
3893 if (k != _ranges.end()) {
3894 /* dragging this point */
3895 _nothing_to_drag = false;
3896 i->points.push_back (p);
3902 if (_nothing_to_drag) {
3906 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3907 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
3912 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3914 if (_nothing_to_drag) {
3918 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3919 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
3921 /* we are ignoring x position for this drag, so we can just pass in anything */
3922 i->line->drag_motion (0, f, true, false);
3927 AutomationRangeDrag::finished (GdkEvent* event, bool)
3929 if (_nothing_to_drag) {
3933 motion (event, false);
3934 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3935 i->line->end_drag ();
3936 i->line->clear_always_in_view ();
3939 _editor->session()->commit_reversible_command ();
3943 AutomationRangeDrag::aborted ()
3945 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3946 i->line->clear_always_in_view ();
3951 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
3954 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
3955 layer = v->region()->layer ();
3956 initial_y = v->get_canvas_group()->property_y ();
3957 initial_playlist = v->region()->playlist ();