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)
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);
1355 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1357 if (!movement_occurred) {
1362 _editor->commit_reversible_command ();
1367 RegionCreateDrag::add_region ()
1369 if (_editor->session()) {
1370 const TempoMap& map (_editor->session()->tempo_map());
1371 framecnt_t pos = grab_frame();
1372 const Meter& m = map.meter_at (pos);
1373 /* not that the frame rate used here can be affected by pull up/down which
1376 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1377 _region = _view->add_region (grab_frame(), len, false);
1382 RegionCreateDrag::aborted ()
1387 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1391 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1395 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1397 Gdk::Cursor* cursor;
1398 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1400 Drag::start_grab (event);
1402 region = &cnote->region_view();
1404 double const region_start = region->get_position_pixels();
1405 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1407 if (grab_x() <= middle_point) {
1408 cursor = _editor->left_side_trim_cursor;
1411 cursor = _editor->right_side_trim_cursor;
1415 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1417 if (event->motion.state & Keyboard::PrimaryModifier) {
1423 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1425 if (ms.size() > 1) {
1426 /* has to be relative, may make no sense otherwise */
1430 /* select this note; if it is already selected, preserve the existing selection,
1431 otherwise make this note the only one selected.
1433 region->note_selected (cnote, cnote->selected ());
1435 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1436 MidiRegionSelection::iterator next;
1439 (*r)->begin_resizing (at_front);
1445 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1447 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1448 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1449 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1454 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1456 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1457 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1458 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1463 NoteResizeDrag::aborted ()
1468 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1471 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1475 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1481 RegionGainDrag::finished (GdkEvent *, bool)
1487 RegionGainDrag::aborted ()
1492 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1493 : RegionDrag (e, i, p, v)
1494 , _have_transaction (false)
1496 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1500 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1503 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1504 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1506 if (tv && tv->is_track()) {
1507 speed = tv->track()->speed();
1510 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1511 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1512 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1514 framepos_t const pf = adjusted_current_frame (event);
1516 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1517 _operation = ContentsTrim;
1518 Drag::start_grab (event, _editor->trimmer_cursor);
1520 /* These will get overridden for a point trim.*/
1521 if (pf < (region_start + region_length/2)) {
1522 /* closer to start */
1523 _operation = StartTrim;
1524 Drag::start_grab (event, _editor->left_side_trim_cursor);
1527 _operation = EndTrim;
1528 Drag::start_grab (event, _editor->right_side_trim_cursor);
1532 switch (_operation) {
1534 _editor->show_verbose_time_cursor (region_start, 10);
1537 _editor->show_verbose_time_cursor (region_end, 10);
1540 _editor->show_verbose_time_cursor (pf, 10);
1546 TrimDrag::motion (GdkEvent* event, bool first_move)
1548 RegionView* rv = _primary;
1550 /* snap modifier works differently here..
1551 its current state has to be passed to the
1552 various trim functions in order to work properly
1556 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1557 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1558 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1560 if (tv && tv->is_track()) {
1561 speed = tv->track()->speed();
1564 framepos_t const pf = adjusted_current_frame (event);
1570 switch (_operation) {
1572 trim_type = "Region start trim";
1575 trim_type = "Region end trim";
1578 trim_type = "Region content trim";
1582 _editor->begin_reversible_command (trim_type);
1583 _have_transaction = true;
1585 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1586 RegionView* rv = i->view;
1587 rv->fake_set_opaque(false);
1588 rv->enable_display (false);
1589 rv->region()->clear_changes ();
1590 rv->region()->suspend_property_changes ();
1592 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1595 arv->temporarily_hide_envelope ();
1598 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1599 insert_result = _editor->motion_frozen_playlists.insert (pl);
1601 if (insert_result.second) {
1607 bool non_overlap_trim = false;
1609 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1610 non_overlap_trim = true;
1613 switch (_operation) {
1615 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1616 _editor->single_start_trim (*i->view, pf, non_overlap_trim);
1621 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1622 _editor->single_end_trim (*i->view, pf, non_overlap_trim);
1628 bool swap_direction = false;
1630 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1631 swap_direction = true;
1634 framecnt_t frame_delta = 0;
1636 bool left_direction = false;
1637 if (last_pointer_frame() > pf) {
1638 left_direction = true;
1641 if (left_direction) {
1642 frame_delta = (last_pointer_frame() - pf);
1644 frame_delta = (pf - last_pointer_frame());
1647 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1648 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1654 switch (_operation) {
1656 _editor->show_verbose_time_cursor((framepos_t) (rv->region()->position()/speed), 10);
1659 _editor->show_verbose_time_cursor((framepos_t) (rv->region()->last_frame()/speed), 10);
1662 _editor->show_verbose_time_cursor (pf, 10);
1669 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1671 if (movement_occurred) {
1672 motion (event, false);
1674 if (!_editor->selection->selected (_primary)) {
1675 _editor->thaw_region_after_trim (*_primary);
1678 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1679 _editor->thaw_region_after_trim (*i->view);
1680 i->view->enable_display (true);
1681 i->view->fake_set_opaque (true);
1682 if (_have_transaction) {
1683 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1687 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1691 _editor->motion_frozen_playlists.clear ();
1693 if (_have_transaction) {
1694 _editor->commit_reversible_command();
1698 /* no mouse movement */
1699 _editor->point_trim (event, adjusted_current_frame (event));
1704 TrimDrag::aborted ()
1706 /* Our motion method is changing model state, so use the Undo system
1707 to cancel. Perhaps not ideal, as this will leave an Undo point
1708 behind which may be slightly odd from the user's point of view.
1713 if (_have_transaction) {
1718 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1722 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1724 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1729 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1732 // create a dummy marker for visual representation of moving the copy.
1733 // The actual copying is not done before we reach the finish callback.
1735 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1736 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1737 *new MeterSection (_marker->meter()));
1739 _item = &new_marker->the_item ();
1740 _marker = new_marker;
1744 MetricSection& section (_marker->meter());
1746 if (!section.movable()) {
1752 Drag::start_grab (event, cursor);
1754 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1756 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1760 MeterMarkerDrag::motion (GdkEvent* event, bool)
1762 framepos_t const pf = adjusted_current_frame (event);
1764 _marker->set_position (pf);
1766 _editor->show_verbose_time_cursor (pf, 10);
1770 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1772 if (!movement_occurred) {
1776 motion (event, false);
1780 TempoMap& map (_editor->session()->tempo_map());
1781 map.bbt_time (last_pointer_frame(), when);
1783 if (_copy == true) {
1784 _editor->begin_reversible_command (_("copy meter mark"));
1785 XMLNode &before = map.get_state();
1786 map.add_meter (_marker->meter(), when);
1787 XMLNode &after = map.get_state();
1788 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1789 _editor->commit_reversible_command ();
1791 // delete the dummy marker we used for visual representation of copying.
1792 // a new visual marker will show up automatically.
1795 _editor->begin_reversible_command (_("move meter mark"));
1796 XMLNode &before = map.get_state();
1797 map.move_meter (_marker->meter(), when);
1798 XMLNode &after = map.get_state();
1799 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1800 _editor->commit_reversible_command ();
1805 MeterMarkerDrag::aborted ()
1807 _marker->set_position (_marker->meter().frame ());
1810 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1814 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1816 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1821 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1826 // create a dummy marker for visual representation of moving the copy.
1827 // The actual copying is not done before we reach the finish callback.
1829 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1830 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
1831 *new TempoSection (_marker->tempo()));
1833 _item = &new_marker->the_item ();
1834 _marker = new_marker;
1838 MetricSection& section (_marker->tempo());
1840 if (!section.movable()) {
1845 Drag::start_grab (event, cursor);
1847 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1848 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1852 TempoMarkerDrag::motion (GdkEvent* event, bool)
1854 framepos_t const pf = adjusted_current_frame (event);
1855 _marker->set_position (pf);
1856 _editor->show_verbose_time_cursor (pf, 10);
1860 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1862 if (!movement_occurred) {
1866 motion (event, false);
1870 TempoMap& map (_editor->session()->tempo_map());
1871 map.bbt_time (last_pointer_frame(), when);
1873 if (_copy == true) {
1874 _editor->begin_reversible_command (_("copy tempo mark"));
1875 XMLNode &before = map.get_state();
1876 map.add_tempo (_marker->tempo(), when);
1877 XMLNode &after = map.get_state();
1878 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1879 _editor->commit_reversible_command ();
1881 // delete the dummy marker we used for visual representation of copying.
1882 // a new visual marker will show up automatically.
1885 _editor->begin_reversible_command (_("move tempo mark"));
1886 XMLNode &before = map.get_state();
1887 map.move_tempo (_marker->tempo(), when);
1888 XMLNode &after = map.get_state();
1889 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1890 _editor->commit_reversible_command ();
1895 TempoMarkerDrag::aborted ()
1897 _marker->set_position (_marker->tempo().frame());
1900 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1904 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
1906 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1911 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1913 Drag::start_grab (event, c);
1917 framepos_t where = _editor->event_frame (event, 0, 0);
1919 _editor->snap_to_with_modifier (where, event);
1920 _editor->playhead_cursor->set_position (where);
1924 if (_cursor == _editor->playhead_cursor) {
1925 _editor->_dragging_playhead = true;
1927 Session* s = _editor->session ();
1930 if (_was_rolling && _stop) {
1934 if (s->is_auditioning()) {
1935 s->cancel_audition ();
1938 s->request_suspend_timecode_transmission ();
1940 if (s->timecode_transmission_suspended ()) {
1941 framepos_t const f = _editor->playhead_cursor->current_frame;
1942 s->send_mmc_locate (f);
1943 s->send_full_time_code (f);
1948 _pointer_frame_offset = raw_grab_frame() - _cursor->current_frame;
1950 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1954 CursorDrag::motion (GdkEvent* event, bool)
1956 framepos_t const adjusted_frame = adjusted_current_frame (event);
1958 if (adjusted_frame == last_pointer_frame()) {
1962 _cursor->set_position (adjusted_frame);
1964 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1966 Session* s = _editor->session ();
1967 if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
1968 framepos_t const f = _editor->playhead_cursor->current_frame;
1969 s->send_mmc_locate (f);
1970 s->send_full_time_code (f);
1975 _editor->update_canvas_now ();
1977 _editor->UpdateAllTransportClocks (_cursor->current_frame);
1981 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
1983 _editor->_dragging_playhead = false;
1985 if (!movement_occurred && _stop) {
1989 motion (event, false);
1991 if (_item == &_editor->playhead_cursor->canvas_item) {
1992 Session* s = _editor->session ();
1994 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
1995 _editor->_pending_locate_request = true;
1996 s->request_resume_timecode_transmission ();
2002 CursorDrag::aborted ()
2004 if (_editor->_dragging_playhead) {
2005 _editor->session()->request_resume_timecode_transmission ();
2006 _editor->_dragging_playhead = false;
2009 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2012 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2013 : RegionDrag (e, i, p, v)
2015 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2019 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2021 Drag::start_grab (event, cursor);
2023 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2024 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2026 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2027 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2029 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2033 FadeInDrag::motion (GdkEvent* event, bool)
2035 framecnt_t fade_length;
2037 framepos_t const pos = adjusted_current_frame (event);
2039 boost::shared_ptr<Region> region = _primary->region ();
2041 if (pos < (region->position() + 64)) {
2042 fade_length = 64; // this should be a minimum defined somewhere
2043 } else if (pos > region->last_frame()) {
2044 fade_length = region->length();
2046 fade_length = pos - region->position();
2049 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2051 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2057 tmp->reset_fade_in_shape_width (fade_length);
2058 tmp->show_fade_line((framecnt_t) fade_length);
2061 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2065 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2067 if (!movement_occurred) {
2071 framecnt_t fade_length;
2073 framepos_t const pos = adjusted_current_frame (event);
2075 boost::shared_ptr<Region> region = _primary->region ();
2077 if (pos < (region->position() + 64)) {
2078 fade_length = 64; // this should be a minimum defined somewhere
2079 } else if (pos > region->last_frame()) {
2080 fade_length = region->length();
2082 fade_length = pos - region->position();
2085 _editor->begin_reversible_command (_("change fade in length"));
2087 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2089 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2095 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2096 XMLNode &before = alist->get_state();
2098 tmp->audio_region()->set_fade_in_length (fade_length);
2099 tmp->audio_region()->set_fade_in_active (true);
2100 tmp->hide_fade_line();
2102 XMLNode &after = alist->get_state();
2103 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2106 _editor->commit_reversible_command ();
2110 FadeInDrag::aborted ()
2112 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2113 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2119 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2120 tmp->hide_fade_line();
2124 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2125 : RegionDrag (e, i, p, v)
2127 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2131 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2133 Drag::start_grab (event, cursor);
2135 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2136 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2138 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2139 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2141 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2145 FadeOutDrag::motion (GdkEvent* event, bool)
2147 framecnt_t fade_length;
2149 framepos_t const pos = adjusted_current_frame (event);
2151 boost::shared_ptr<Region> region = _primary->region ();
2153 if (pos > (region->last_frame() - 64)) {
2154 fade_length = 64; // this should really be a minimum fade defined somewhere
2156 else if (pos < region->position()) {
2157 fade_length = region->length();
2160 fade_length = region->last_frame() - pos;
2163 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2165 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2171 tmp->reset_fade_out_shape_width (fade_length);
2172 tmp->show_fade_line(region->length() - fade_length);
2175 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2179 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2181 if (!movement_occurred) {
2185 framecnt_t fade_length;
2187 framepos_t const pos = adjusted_current_frame (event);
2189 boost::shared_ptr<Region> region = _primary->region ();
2191 if (pos > (region->last_frame() - 64)) {
2192 fade_length = 64; // this should really be a minimum fade defined somewhere
2194 else if (pos < region->position()) {
2195 fade_length = region->length();
2198 fade_length = region->last_frame() - pos;
2201 _editor->begin_reversible_command (_("change fade out length"));
2203 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2205 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2211 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2212 XMLNode &before = alist->get_state();
2214 tmp->audio_region()->set_fade_out_length (fade_length);
2215 tmp->audio_region()->set_fade_out_active (true);
2216 tmp->hide_fade_line();
2218 XMLNode &after = alist->get_state();
2219 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2222 _editor->commit_reversible_command ();
2226 FadeOutDrag::aborted ()
2228 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2229 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2235 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2236 tmp->hide_fade_line();
2240 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2243 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2245 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2248 _points.push_back (Gnome::Art::Point (0, 0));
2249 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2251 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2252 _line->property_width_pixels() = 1;
2253 _line->property_points () = _points;
2256 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2259 MarkerDrag::~MarkerDrag ()
2261 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2267 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2269 Drag::start_grab (event, cursor);
2273 Location *location = _editor->find_location_from_marker (_marker, is_start);
2274 _editor->_dragging_edit_point = true;
2276 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2278 update_item (location);
2280 // _drag_line->show();
2281 // _line->raise_to_top();
2284 _editor->show_verbose_time_cursor (location->start(), 10);
2286 _editor->show_verbose_time_cursor (location->end(), 10);
2289 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2292 case Selection::Toggle:
2293 _editor->selection->toggle (_marker);
2295 case Selection::Set:
2296 if (!_editor->selection->selected (_marker)) {
2297 _editor->selection->set (_marker);
2300 case Selection::Extend:
2302 Locations::LocationList ll;
2303 list<Marker*> to_add;
2305 _editor->selection->markers.range (s, e);
2306 s = min (_marker->position(), s);
2307 e = max (_marker->position(), e);
2310 if (e < max_frames) {
2313 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2314 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2315 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2318 to_add.push_back (lm->start);
2321 to_add.push_back (lm->end);
2325 if (!to_add.empty()) {
2326 _editor->selection->add (to_add);
2330 case Selection::Add:
2331 _editor->selection->add (_marker);
2335 /* Set up copies for us to manipulate during the drag */
2337 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2338 Location* l = _editor->find_location_from_marker (*i, is_start);
2339 _copied_locations.push_back (new Location (*l));
2344 MarkerDrag::motion (GdkEvent* event, bool)
2346 framecnt_t f_delta = 0;
2348 bool move_both = false;
2350 Location *real_location;
2351 Location *copy_location = 0;
2353 framepos_t const newframe = adjusted_current_frame (event);
2355 framepos_t next = newframe;
2357 if (newframe == last_pointer_frame()) {
2361 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2365 MarkerSelection::iterator i;
2366 list<Location*>::iterator x;
2368 /* find the marker we're dragging, and compute the delta */
2370 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2371 x != _copied_locations.end() && i != _editor->selection->markers.end();
2377 if (marker == _marker) {
2379 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2384 if (real_location->is_mark()) {
2385 f_delta = newframe - copy_location->start();
2389 switch (marker->type()) {
2391 case Marker::LoopStart:
2392 case Marker::PunchIn:
2393 f_delta = newframe - copy_location->start();
2397 case Marker::LoopEnd:
2398 case Marker::PunchOut:
2399 f_delta = newframe - copy_location->end();
2402 /* what kind of marker is this ? */
2410 if (i == _editor->selection->markers.end()) {
2411 /* hmm, impossible - we didn't find the dragged marker */
2415 /* now move them all */
2417 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2418 x != _copied_locations.end() && i != _editor->selection->markers.end();
2424 /* call this to find out if its the start or end */
2426 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2430 if (real_location->locked()) {
2434 if (copy_location->is_mark()) {
2438 copy_location->set_start (copy_location->start() + f_delta);
2442 framepos_t new_start = copy_location->start() + f_delta;
2443 framepos_t new_end = copy_location->end() + f_delta;
2445 if (is_start) { // start-of-range marker
2448 copy_location->set_start (new_start);
2449 copy_location->set_end (new_end);
2450 } else if (new_start < copy_location->end()) {
2451 copy_location->set_start (new_start);
2453 _editor->snap_to (next, 1, true);
2454 copy_location->set_end (next);
2455 copy_location->set_start (newframe);
2458 } else { // end marker
2461 copy_location->set_end (new_end);
2462 copy_location->set_start (new_start);
2463 } else if (new_end > copy_location->start()) {
2464 copy_location->set_end (new_end);
2465 } else if (newframe > 0) {
2466 _editor->snap_to (next, -1, true);
2467 copy_location->set_start (next);
2468 copy_location->set_end (newframe);
2473 update_item (copy_location);
2475 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2478 lm->set_position (copy_location->start(), copy_location->end());
2482 assert (!_copied_locations.empty());
2484 _editor->show_verbose_time_cursor (newframe, 10);
2487 _editor->update_canvas_now ();
2492 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2494 if (!movement_occurred) {
2496 /* just a click, do nothing but finish
2497 off the selection process
2500 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2503 case Selection::Set:
2504 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2505 _editor->selection->set (_marker);
2509 case Selection::Toggle:
2510 case Selection::Extend:
2511 case Selection::Add:
2518 _editor->_dragging_edit_point = false;
2520 _editor->begin_reversible_command ( _("move marker") );
2521 XMLNode &before = _editor->session()->locations()->get_state();
2523 MarkerSelection::iterator i;
2524 list<Location*>::iterator x;
2527 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2528 x != _copied_locations.end() && i != _editor->selection->markers.end();
2531 Location * location = _editor->find_location_from_marker (*i, is_start);
2535 if (location->locked()) {
2539 if (location->is_mark()) {
2540 location->set_start ((*x)->start());
2542 location->set ((*x)->start(), (*x)->end());
2547 XMLNode &after = _editor->session()->locations()->get_state();
2548 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2549 _editor->commit_reversible_command ();
2555 MarkerDrag::aborted ()
2561 MarkerDrag::update_item (Location* location)
2563 double const x1 = _editor->frame_to_pixel (location->start());
2565 _points.front().set_x(x1);
2566 _points.back().set_x(x1);
2567 _line->property_points() = _points;
2570 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2572 _cumulative_x_drag (0),
2573 _cumulative_y_drag (0)
2575 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2577 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2583 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2585 Drag::start_grab (event, _editor->fader_cursor);
2587 // start the grab at the center of the control point so
2588 // the point doesn't 'jump' to the mouse after the first drag
2589 _fixed_grab_x = _point->get_x();
2590 _fixed_grab_y = _point->get_y();
2592 float const fraction = 1 - (_point->get_y() / _point->line().height());
2594 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2596 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2597 event->button.x + 10, event->button.y + 10);
2599 _editor->show_verbose_canvas_cursor ();
2603 ControlPointDrag::motion (GdkEvent* event, bool)
2605 double dx = _drags->current_pointer_x() - last_pointer_x();
2606 double dy = _drags->current_pointer_y() - last_pointer_y();
2608 if (event->button.state & Keyboard::SecondaryModifier) {
2613 /* coordinate in pixels relative to the start of the region (for region-based automation)
2614 or track (for track-based automation) */
2615 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2616 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2618 // calculate zero crossing point. back off by .01 to stay on the
2619 // positive side of zero
2620 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2622 // make sure we hit zero when passing through
2623 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2627 if (_x_constrained) {
2630 if (_y_constrained) {
2634 _cumulative_x_drag = cx - _fixed_grab_x;
2635 _cumulative_y_drag = cy - _fixed_grab_y;
2639 cy = min ((double) _point->line().height(), cy);
2641 framepos_t cx_frames = _editor->unit_to_frame (cx);
2643 if (!_x_constrained) {
2644 _editor->snap_to_with_modifier (cx_frames, event);
2647 cx_frames = min (cx_frames, _point->line().maximum_time());
2649 float const fraction = 1.0 - (cy / _point->line().height());
2651 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2653 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2655 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2659 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2661 if (!movement_occurred) {
2665 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2666 _editor->reset_point_selection ();
2670 motion (event, false);
2673 _point->line().end_drag ();
2674 _editor->session()->commit_reversible_command ();
2678 ControlPointDrag::aborted ()
2680 _point->line().reset ();
2684 ControlPointDrag::active (Editing::MouseMode m)
2686 if (m == Editing::MouseGain) {
2687 /* always active in mouse gain */
2691 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2692 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2695 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2698 _cumulative_y_drag (0)
2700 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2704 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2706 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2709 _item = &_line->grab_item ();
2711 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2712 origin, and ditto for y.
2715 double cx = event->button.x;
2716 double cy = event->button.y;
2718 _line->parent_group().w2i (cx, cy);
2720 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2725 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2726 /* no adjacent points */
2730 Drag::start_grab (event, _editor->fader_cursor);
2732 /* store grab start in parent frame */
2737 double fraction = 1.0 - (cy / _line->height());
2739 _line->start_drag_line (before, after, fraction);
2741 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2742 event->button.x + 10, event->button.y + 10);
2744 _editor->show_verbose_canvas_cursor ();
2748 LineDrag::motion (GdkEvent* event, bool)
2750 double dy = _drags->current_pointer_y() - last_pointer_y();
2752 if (event->button.state & Keyboard::SecondaryModifier) {
2756 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2758 _cumulative_y_drag = cy - _fixed_grab_y;
2761 cy = min ((double) _line->height(), cy);
2763 double const fraction = 1.0 - (cy / _line->height());
2767 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2773 /* we are ignoring x position for this drag, so we can just pass in anything */
2774 _line->drag_motion (0, fraction, true, push);
2776 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2780 LineDrag::finished (GdkEvent* event, bool)
2782 motion (event, false);
2784 _editor->session()->commit_reversible_command ();
2788 LineDrag::aborted ()
2793 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2796 _cumulative_x_drag (0)
2798 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2802 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2804 Drag::start_grab (event);
2806 _line = reinterpret_cast<SimpleLine*> (_item);
2809 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2811 double cx = event->button.x;
2812 double cy = event->button.y;
2814 _item->property_parent().get_value()->w2i(cx, cy);
2816 /* store grab start in parent frame */
2817 _region_view_grab_x = cx;
2819 _before = _line->property_x1();
2821 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2823 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2827 FeatureLineDrag::motion (GdkEvent* event, bool)
2829 double dx = _drags->current_pointer_x() - last_pointer_x();
2831 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2833 _cumulative_x_drag += dx;
2835 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2844 _line->property_x1() = cx;
2845 _line->property_x2() = cx;
2847 _before = _line->property_x1();
2851 FeatureLineDrag::finished (GdkEvent* event, bool)
2853 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2854 _arv->update_transient(_before, _line->property_x1());
2858 FeatureLineDrag::aborted ()
2863 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2866 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2870 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2872 Drag::start_grab (event);
2873 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2877 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2884 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2886 framepos_t grab = grab_frame ();
2887 if (Config->get_rubberbanding_snaps_to_grid ()) {
2888 _editor->snap_to_with_modifier (grab, event);
2891 /* base start and end on initial click position */
2901 if (_drags->current_pointer_y() < grab_y()) {
2902 y1 = _drags->current_pointer_y();
2905 y2 = _drags->current_pointer_y();
2910 if (start != end || y1 != y2) {
2912 double x1 = _editor->frame_to_pixel (start);
2913 double x2 = _editor->frame_to_pixel (end);
2915 _editor->rubberband_rect->property_x1() = x1;
2916 _editor->rubberband_rect->property_y1() = y1;
2917 _editor->rubberband_rect->property_x2() = x2;
2918 _editor->rubberband_rect->property_y2() = y2;
2920 _editor->rubberband_rect->show();
2921 _editor->rubberband_rect->raise_to_top();
2923 _editor->show_verbose_time_cursor (pf, 10);
2928 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2930 if (movement_occurred) {
2932 motion (event, false);
2935 if (_drags->current_pointer_y() < grab_y()) {
2936 y1 = _drags->current_pointer_y();
2939 y2 = _drags->current_pointer_y();
2944 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2947 _editor->begin_reversible_command (_("rubberband selection"));
2949 if (grab_frame() < last_pointer_frame()) {
2950 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
2952 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
2956 _editor->commit_reversible_command ();
2960 if (!getenv("ARDOUR_SAE")) {
2961 _editor->selection->clear_tracks();
2963 _editor->selection->clear_regions();
2964 _editor->selection->clear_points ();
2965 _editor->selection->clear_lines ();
2968 _editor->rubberband_rect->hide();
2972 RubberbandSelectDrag::aborted ()
2974 _editor->rubberband_rect->hide ();
2977 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
2978 : RegionDrag (e, i, p, v)
2980 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
2984 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2986 Drag::start_grab (event, cursor);
2988 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2992 TimeFXDrag::motion (GdkEvent* event, bool)
2994 RegionView* rv = _primary;
2996 framepos_t const pf = adjusted_current_frame (event);
2998 if (pf > rv->region()->position()) {
2999 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3002 _editor->show_verbose_time_cursor (pf, 10);
3006 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3008 _primary->get_time_axis_view().hide_timestretch ();
3010 if (!movement_occurred) {
3014 if (last_pointer_frame() < _primary->region()->position()) {
3015 /* backwards drag of the left edge - not usable */
3019 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3021 float percentage = (double) newlen / (double) _primary->region()->length();
3023 #ifndef USE_RUBBERBAND
3024 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3025 if (_primary->region()->data_type() == DataType::AUDIO) {
3026 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3030 _editor->begin_reversible_command (_("timestretch"));
3032 // XXX how do timeFX on multiple regions ?
3037 if (_editor->time_stretch (rs, percentage) == -1) {
3038 error << _("An error occurred while executing time stretch operation") << endmsg;
3043 TimeFXDrag::aborted ()
3045 _primary->get_time_axis_view().hide_timestretch ();
3048 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3051 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3055 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3057 Drag::start_grab (event);
3061 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3063 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3067 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3069 if (movement_occurred && _editor->session()) {
3070 /* make sure we stop */
3071 _editor->session()->request_transport_speed (0.0);
3076 ScrubDrag::aborted ()
3081 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3085 , _original_pointer_time_axis (-1)
3086 , _last_pointer_time_axis (-1)
3088 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3092 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3094 framepos_t start = 0;
3097 if (_editor->session() == 0) {
3101 Gdk::Cursor* cursor = 0;
3103 switch (_operation) {
3104 case CreateSelection:
3105 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3110 cursor = _editor->selector_cursor;
3111 Drag::start_grab (event, cursor);
3114 case SelectionStartTrim:
3115 if (_editor->clicked_axisview) {
3116 _editor->clicked_axisview->order_selection_trims (_item, true);
3118 Drag::start_grab (event, _editor->left_side_trim_cursor);
3119 start = _editor->selection->time[_editor->clicked_selection].start;
3120 _pointer_frame_offset = raw_grab_frame() - start;
3123 case SelectionEndTrim:
3124 if (_editor->clicked_axisview) {
3125 _editor->clicked_axisview->order_selection_trims (_item, false);
3127 Drag::start_grab (event, _editor->right_side_trim_cursor);
3128 end = _editor->selection->time[_editor->clicked_selection].end;
3129 _pointer_frame_offset = raw_grab_frame() - end;
3133 start = _editor->selection->time[_editor->clicked_selection].start;
3134 Drag::start_grab (event, cursor);
3135 _pointer_frame_offset = raw_grab_frame() - start;
3139 if (_operation == SelectionMove) {
3140 _editor->show_verbose_time_cursor (start, 10);
3142 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3145 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3149 SelectionDrag::motion (GdkEvent* event, bool first_move)
3151 framepos_t start = 0;
3155 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3156 if (pending_time_axis.first == 0) {
3160 framepos_t const pending_position = adjusted_current_frame (event);
3162 /* only alter selection if things have changed */
3164 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3168 switch (_operation) {
3169 case CreateSelection:
3171 framepos_t grab = grab_frame ();
3174 _editor->snap_to (grab);
3177 if (pending_position < grab_frame()) {
3178 start = pending_position;
3181 end = pending_position;
3185 /* first drag: Either add to the selection
3186 or create a new selection
3192 /* adding to the selection */
3193 _editor->set_selected_track_as_side_effect (Selection::Add);
3194 //_editor->selection->add (_editor->clicked_axisview);
3195 _editor->clicked_selection = _editor->selection->add (start, end);
3200 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3201 //_editor->selection->set (_editor->clicked_axisview);
3202 _editor->set_selected_track_as_side_effect (Selection::Set);
3205 _editor->clicked_selection = _editor->selection->set (start, end);
3209 /* select the track that we're in */
3210 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3211 // _editor->set_selected_track_as_side_effect (Selection::Add);
3212 _editor->selection->add (pending_time_axis.first);
3213 _added_time_axes.push_back (pending_time_axis.first);
3216 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3217 tracks that we selected in the first place.
3220 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3221 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3223 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3224 while (i != _added_time_axes.end()) {
3226 list<TimeAxisView*>::iterator tmp = i;
3229 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3230 _editor->selection->remove (*i);
3231 _added_time_axes.remove (*i);
3240 case SelectionStartTrim:
3242 start = _editor->selection->time[_editor->clicked_selection].start;
3243 end = _editor->selection->time[_editor->clicked_selection].end;
3245 if (pending_position > end) {
3248 start = pending_position;
3252 case SelectionEndTrim:
3254 start = _editor->selection->time[_editor->clicked_selection].start;
3255 end = _editor->selection->time[_editor->clicked_selection].end;
3257 if (pending_position < start) {
3260 end = pending_position;
3267 start = _editor->selection->time[_editor->clicked_selection].start;
3268 end = _editor->selection->time[_editor->clicked_selection].end;
3270 length = end - start;
3272 start = pending_position;
3273 _editor->snap_to (start);
3275 end = start + length;
3280 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3281 _editor->start_canvas_autoscroll (1, 0);
3285 _editor->selection->replace (_editor->clicked_selection, start, end);
3288 if (_operation == SelectionMove) {
3289 _editor->show_verbose_time_cursor(start, 10);
3291 _editor->show_verbose_time_cursor(pending_position, 10);
3296 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3298 Session* s = _editor->session();
3300 if (movement_occurred) {
3301 motion (event, false);
3302 /* XXX this is not object-oriented programming at all. ick */
3303 if (_editor->selection->time.consolidate()) {
3304 _editor->selection->TimeChanged ();
3307 /* XXX what if its a music time selection? */
3308 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3309 s->request_play_range (&_editor->selection->time, true);
3314 /* just a click, no pointer movement.*/
3316 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3317 _editor->selection->clear_time();
3320 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3321 _editor->selection->set (_editor->clicked_axisview);
3324 if (s && s->get_play_range () && s->transport_rolling()) {
3325 s->request_stop (false, false);
3330 _editor->stop_canvas_autoscroll ();
3334 SelectionDrag::aborted ()
3339 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3344 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3346 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3347 physical_screen_height (_editor->get_window()));
3348 _drag_rect->hide ();
3350 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3351 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3355 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3357 if (_editor->session() == 0) {
3361 Gdk::Cursor* cursor = 0;
3363 if (!_editor->temp_location) {
3364 _editor->temp_location = new Location (*_editor->session());
3367 switch (_operation) {
3368 case CreateRangeMarker:
3369 case CreateTransportMarker:
3370 case CreateCDMarker:
3372 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3377 cursor = _editor->selector_cursor;
3381 Drag::start_grab (event, cursor);
3383 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3387 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3389 framepos_t start = 0;
3391 ArdourCanvas::SimpleRect *crect;
3393 switch (_operation) {
3394 case CreateRangeMarker:
3395 crect = _editor->range_bar_drag_rect;
3397 case CreateTransportMarker:
3398 crect = _editor->transport_bar_drag_rect;
3400 case CreateCDMarker:
3401 crect = _editor->cd_marker_bar_drag_rect;
3404 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3409 framepos_t const pf = adjusted_current_frame (event);
3411 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3412 framepos_t grab = grab_frame ();
3413 _editor->snap_to (grab);
3415 if (pf < grab_frame()) {
3423 /* first drag: Either add to the selection
3424 or create a new selection.
3429 _editor->temp_location->set (start, end);
3433 update_item (_editor->temp_location);
3435 //_drag_rect->raise_to_top();
3440 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3441 _editor->start_canvas_autoscroll (1, 0);
3445 _editor->temp_location->set (start, end);
3447 double x1 = _editor->frame_to_pixel (start);
3448 double x2 = _editor->frame_to_pixel (end);
3449 crect->property_x1() = x1;
3450 crect->property_x2() = x2;
3452 update_item (_editor->temp_location);
3455 _editor->show_verbose_time_cursor (pf, 10);
3460 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3462 Location * newloc = 0;
3466 if (movement_occurred) {
3467 motion (event, false);
3470 switch (_operation) {
3471 case CreateRangeMarker:
3472 case CreateCDMarker:
3474 _editor->begin_reversible_command (_("new range marker"));
3475 XMLNode &before = _editor->session()->locations()->get_state();
3476 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3477 if (_operation == CreateCDMarker) {
3478 flags = Location::IsRangeMarker | Location::IsCDMarker;
3479 _editor->cd_marker_bar_drag_rect->hide();
3482 flags = Location::IsRangeMarker;
3483 _editor->range_bar_drag_rect->hide();
3485 newloc = new Location (
3486 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3489 _editor->session()->locations()->add (newloc, true);
3490 XMLNode &after = _editor->session()->locations()->get_state();
3491 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3492 _editor->commit_reversible_command ();
3496 case CreateTransportMarker:
3497 // popup menu to pick loop or punch
3498 _editor->new_transport_marker_context_menu (&event->button, _item);
3502 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3504 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3509 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3511 if (end == max_frames) {
3512 end = _editor->session()->current_end_frame ();
3515 if (start == max_frames) {
3516 start = _editor->session()->current_start_frame ();
3519 switch (_editor->mouse_mode) {
3521 /* find the two markers on either side and then make the selection from it */
3522 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3526 /* find the two markers on either side of the click and make the range out of it */
3527 _editor->selection->set (start, end);
3536 _editor->stop_canvas_autoscroll ();
3540 RangeMarkerBarDrag::aborted ()
3546 RangeMarkerBarDrag::update_item (Location* location)
3548 double const x1 = _editor->frame_to_pixel (location->start());
3549 double const x2 = _editor->frame_to_pixel (location->end());
3551 _drag_rect->property_x1() = x1;
3552 _drag_rect->property_x2() = x2;
3555 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3558 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3562 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3564 Drag::start_grab (event, _editor->zoom_cursor);
3565 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3569 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3574 framepos_t const pf = adjusted_current_frame (event);
3576 framepos_t grab = grab_frame ();
3577 _editor->snap_to_with_modifier (grab, event);
3579 /* base start and end on initial click position */
3591 _editor->zoom_rect->show();
3592 _editor->zoom_rect->raise_to_top();
3595 _editor->reposition_zoom_rect(start, end);
3597 _editor->show_verbose_time_cursor (pf, 10);
3602 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3604 if (movement_occurred) {
3605 motion (event, false);
3607 if (grab_frame() < last_pointer_frame()) {
3608 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3610 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3613 _editor->temporal_zoom_to_frame (false, grab_frame());
3615 temporal_zoom_step (false);
3616 center_screen (grab_frame());
3620 _editor->zoom_rect->hide();
3624 MouseZoomDrag::aborted ()
3626 _editor->zoom_rect->hide ();
3629 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3631 , _cumulative_dx (0)
3632 , _cumulative_dy (0)
3634 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3636 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3637 _region = &_primary->region_view ();
3638 _note_height = _region->midi_stream_view()->note_height ();
3642 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3644 Drag::start_grab (event);
3646 if (!(_was_selected = _primary->selected())) {
3648 /* tertiary-click means extend selection - we'll do that on button release,
3649 so don't add it here, because otherwise we make it hard to figure
3650 out the "extend-to" range.
3653 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3656 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3659 _region->note_selected (_primary, true);
3661 _region->unique_select (_primary);
3667 /** @return Current total drag x change in frames */
3669 NoteDrag::total_dx () const
3672 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3674 /* primary note time */
3675 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3677 /* new time of the primary note relative to the region position */
3678 frameoffset_t const st = n + dx;
3680 /* snap and return corresponding delta */
3681 return _region->snap_frame_to_frame (st) - n;
3684 /** @return Current total drag y change in notes */
3686 NoteDrag::total_dy () const
3688 /* this is `backwards' to make increasing note number go in the right direction */
3689 double const dy = _drags->current_pointer_y() - grab_y();
3694 if (abs (dy) >= _note_height) {
3696 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3698 ndy = (int8_t) floor (dy / _note_height / 2.0);
3707 NoteDrag::motion (GdkEvent *, bool)
3709 /* Total change in x and y since the start of the drag */
3710 frameoffset_t const dx = total_dx ();
3711 int8_t const dy = total_dy ();
3713 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3714 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3715 double const tdy = dy * _note_height - _cumulative_dy;
3718 _region->move_selection (tdx, tdy);
3719 _cumulative_dx += tdx;
3720 _cumulative_dy += tdy;
3723 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + dy).c_str(),
3724 (int) floor (_primary->note()->note() + dy));
3726 _editor->show_verbose_canvas_cursor_with (buf);
3731 NoteDrag::finished (GdkEvent* ev, bool moved)
3734 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3736 if (_was_selected) {
3737 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3739 _region->note_deselected (_primary);
3742 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3743 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3745 if (!extend && !add && _region->selection_size() > 1) {
3746 _region->unique_select (_primary);
3747 } else if (extend) {
3748 _region->note_selected (_primary, true, true);
3750 /* it was added during button press */
3755 _region->note_dropped (_primary, total_dx(), - total_dy());
3760 NoteDrag::aborted ()
3765 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3766 : Drag (editor, item)
3768 , _nothing_to_drag (false)
3770 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3772 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3775 /* get all lines in the automation view */
3776 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3778 /* find those that overlap the ranges being dragged */
3779 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3780 while (i != lines.end ()) {
3781 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3784 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3786 /* check this range against all the AudioRanges that we are using */
3787 list<AudioRange>::const_iterator k = _ranges.begin ();
3788 while (k != _ranges.end()) {
3789 if (k->coverage (r.first, r.second) != OverlapNone) {
3795 /* add it to our list if it overlaps at all */
3796 if (k != _ranges.end()) {
3801 _lines.push_back (n);
3807 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3811 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3813 Drag::start_grab (event, cursor);
3815 /* Get line states before we start changing things */
3816 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3817 i->state = &i->line->get_state ();
3820 if (_ranges.empty()) {
3822 /* No selected time ranges: drag all points */
3823 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3824 uint32_t const N = i->line->npoints ();
3825 for (uint32_t j = 0; j < N; ++j) {
3826 i->points.push_back (i->line->nth (j));
3832 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3834 framecnt_t const half = (i->start + i->end) / 2;
3836 /* find the line that this audio range starts in */
3837 list<Line>::iterator j = _lines.begin();
3838 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3842 if (j != _lines.end()) {
3843 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3845 /* j is the line that this audio range starts in; fade into it;
3846 64 samples length plucked out of thin air.
3849 framepos_t a = i->start + 64;
3854 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3855 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3857 the_list->add (p, the_list->eval (p));
3858 j->line->add_always_in_view (p);
3859 the_list->add (q, the_list->eval (q));
3860 j->line->add_always_in_view (q);
3863 /* same thing for the end */
3866 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3870 if (j != _lines.end()) {
3871 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3873 /* j is the line that this audio range starts in; fade out of it;
3874 64 samples length plucked out of thin air.
3877 framepos_t b = i->end - 64;
3882 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3883 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
3885 the_list->add (p, the_list->eval (p));
3886 j->line->add_always_in_view (p);
3887 the_list->add (q, the_list->eval (q));
3888 j->line->add_always_in_view (q);
3892 _nothing_to_drag = true;
3894 /* Find all the points that should be dragged and put them in the relevant
3895 points lists in the Line structs.
3898 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3900 uint32_t const N = i->line->npoints ();
3901 for (uint32_t j = 0; j < N; ++j) {
3903 /* here's a control point on this line */
3904 ControlPoint* p = i->line->nth (j);
3905 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
3907 /* see if it's inside a range */
3908 list<AudioRange>::const_iterator k = _ranges.begin ();
3909 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
3913 if (k != _ranges.end()) {
3914 /* dragging this point */
3915 _nothing_to_drag = false;
3916 i->points.push_back (p);
3922 if (_nothing_to_drag) {
3926 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3927 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
3932 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3934 if (_nothing_to_drag) {
3938 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3939 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
3941 /* we are ignoring x position for this drag, so we can just pass in anything */
3942 i->line->drag_motion (0, f, true, false);
3947 AutomationRangeDrag::finished (GdkEvent* event, bool)
3949 if (_nothing_to_drag) {
3953 motion (event, false);
3954 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3955 i->line->end_drag ();
3956 i->line->clear_always_in_view ();
3959 _editor->session()->commit_reversible_command ();
3963 AutomationRangeDrag::aborted ()
3965 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3966 i->line->clear_always_in_view ();
3971 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
3974 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
3975 layer = v->region()->layer ();
3976 initial_y = v->get_canvas_group()->property_y ();
3977 initial_playlist = v->region()->playlist ();