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 /* the list of views can be empty at this point if this is a region list-insert drag
372 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
373 _views.push_back (DraggingView (*i, this));
376 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
380 RegionDrag::region_going_away (RegionView* v)
382 list<DraggingView>::iterator i = _views.begin ();
383 while (i != _views.end() && i->view != v) {
387 if (i != _views.end()) {
392 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
394 RegionDrag::find_time_axis_view (TimeAxisView* t) const
397 int const N = _time_axis_views.size ();
398 while (i < N && _time_axis_views[i] != t) {
409 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
410 : RegionDrag (e, i, p, v),
419 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
421 Drag::start_grab (event, cursor);
423 _editor->show_verbose_time_cursor (_last_frame_position, 10);
425 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
426 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
427 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
431 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
433 /* compute the amount of pointer motion in frames, and where
434 the region would be if we moved it by that much.
436 *pending_region_position = adjusted_current_frame (event);
438 framepos_t sync_frame;
439 framecnt_t sync_offset;
442 sync_offset = _primary->region()->sync_offset (sync_dir);
444 /* we don't handle a sync point that lies before zero.
446 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
448 sync_frame = *pending_region_position + (sync_dir*sync_offset);
450 _editor->snap_to_with_modifier (sync_frame, event);
452 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
455 *pending_region_position = _last_frame_position;
458 if (*pending_region_position > max_framepos - _primary->region()->length()) {
459 *pending_region_position = _last_frame_position;
464 /* in locked edit mode, reverse the usual meaning of _x_constrained */
465 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
467 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
469 /* x movement since last time */
470 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
472 /* total x movement */
473 framecnt_t total_dx = *pending_region_position;
474 if (regions_came_from_canvas()) {
475 total_dx = total_dx - grab_frame () + _pointer_frame_offset;
478 /* check that no regions have gone off the start of the session */
479 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
480 if ((i->view->region()->position() + total_dx) < 0) {
482 *pending_region_position = _last_frame_position;
487 _last_frame_position = *pending_region_position;
494 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
496 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
497 int const n = i->time_axis_view + delta_track;
498 if (n < 0 || n >= int (_time_axis_views.size())) {
499 /* off the top or bottom track */
503 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
504 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
505 /* not a track, or the wrong type */
509 int const l = i->layer + delta_layer;
510 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
511 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
512 If it has, the layers will be munged later anyway, so it's ok.
518 /* all regions being dragged are ok with this change */
523 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
525 /* Find the TimeAxisView that the pointer is now over */
526 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
528 /* Bail early if we're not over a track */
529 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
530 if (!rtv || !rtv->is_track()) {
531 _editor->hide_verbose_canvas_cursor ();
535 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
537 /* Here's the current pointer position in terms of time axis view and layer */
538 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
539 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
541 /* Work out the change in x */
542 framepos_t pending_region_position;
543 double const x_delta = compute_x_delta (event, &pending_region_position);
545 /* Work out the change in y */
546 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
547 int delta_layer = current_pointer_layer - _last_pointer_layer;
549 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
550 /* this y movement is not allowed, so do no y movement this time */
551 delta_time_axis_view = 0;
555 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
556 /* haven't reached next snap point, and we're not switching
557 trackviews nor layers. nothing to do.
562 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
564 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
566 RegionView* rv = i->view;
568 if (rv->region()->locked()) {
574 /* here we are calculating the y distance from the
575 top of the first track view to the top of the region
576 area of the track view that we're working on */
578 /* this x value is just a dummy value so that we have something
583 /* distance from the top of this track view to the region area
584 of our track view is always 1 */
588 /* convert to world coordinates, ie distance from the top of
591 rv->get_canvas_frame()->i2w (ix1, iy1);
593 /* compensate for the ruler section and the vertical scrollbar position */
594 iy1 += _editor->get_trackview_group_vertical_offset ();
596 // hide any dependent views
598 rv->get_time_axis_view().hide_dependent_views (*rv);
601 reparent to a non scrolling group so that we can keep the
602 region selection above all time axis views.
603 reparenting means we have to move the rv as the two
604 parent groups have different coordinates.
607 rv->get_canvas_group()->property_y() = iy1 - 1;
608 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
610 rv->fake_set_opaque (true);
613 /* Work out the change in y position of this region view */
617 /* If we have moved tracks, we'll fudge the layer delta so that the
618 region gets moved back onto layer 0 on its new track; this avoids
619 confusion when dragging regions from non-zero layers onto different
622 int this_delta_layer = delta_layer;
623 if (delta_time_axis_view != 0) {
624 this_delta_layer = - i->layer;
627 /* Move this region to layer 0 on its old track */
628 StreamView* lv = _time_axis_views[i->time_axis_view]->view ();
629 if (lv->layer_display() == Stacked) {
630 y_delta -= (lv->layers() - i->layer - 1) * lv->child_height ();
633 /* Now move it to its right layer on the current track */
634 StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view ();
635 if (cv->layer_display() == Stacked) {
636 y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height ();
640 if (delta_time_axis_view > 0) {
641 for (int j = 0; j < delta_time_axis_view; ++j) {
642 y_delta += _time_axis_views[i->time_axis_view + j]->current_height ();
645 /* start by subtracting the height of the track above where we are now */
646 for (int j = 1; j <= -delta_time_axis_view; ++j) {
647 y_delta -= _time_axis_views[i->time_axis_view - j]->current_height ();
652 rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ());
654 /* Update the DraggingView */
655 i->time_axis_view += delta_time_axis_view;
656 i->layer += this_delta_layer;
659 _editor->mouse_brush_insert_region (rv, pending_region_position);
661 rv->move (x_delta, y_delta);
664 } /* foreach region */
666 _total_x_delta += x_delta;
669 _editor->cursor_group->raise_to_top();
672 if (x_delta != 0 && !_brushing) {
673 _editor->show_verbose_time_cursor (_last_frame_position, 10);
676 _last_pointer_time_axis_view += delta_time_axis_view;
677 _last_pointer_layer += delta_layer;
681 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
683 if (_copy && first_move) {
685 /* duplicate the regionview(s) and region(s) */
687 list<DraggingView> new_regionviews;
689 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
691 RegionView* rv = i->view;
692 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
693 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
695 const boost::shared_ptr<const Region> original = rv->region();
696 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
697 region_copy->set_position (original->position(), this);
701 boost::shared_ptr<AudioRegion> audioregion_copy
702 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
704 nrv = new AudioRegionView (*arv, audioregion_copy);
706 boost::shared_ptr<MidiRegion> midiregion_copy
707 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
708 nrv = new MidiRegionView (*mrv, midiregion_copy);
713 nrv->get_canvas_group()->show ();
714 new_regionviews.push_back (DraggingView (nrv, this));
716 /* swap _primary to the copy */
718 if (rv == _primary) {
722 /* ..and deselect the one we copied */
724 rv->set_selected (false);
727 if (!new_regionviews.empty()) {
729 /* reflect the fact that we are dragging the copies */
731 _views = new_regionviews;
733 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
736 sync the canvas to what we think is its current state
737 without it, the canvas seems to
738 "forget" to update properly after the upcoming reparent()
739 ..only if the mouse is in rapid motion at the time of the grab.
740 something to do with regionview creation taking so long?
742 _editor->update_canvas_now();
746 RegionMotionDrag::motion (event, first_move);
750 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
752 if (!movement_occurred) {
757 /* reverse this here so that we have the correct logic to finalize
761 if (Config->get_edit_mode() == Lock) {
762 _x_constrained = !_x_constrained;
765 assert (!_views.empty ());
767 bool const changed_position = (_last_frame_position != _primary->region()->position());
768 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
769 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
771 _editor->update_canvas_now ();
793 RegionMoveDrag::finished_copy (
794 bool const changed_position,
795 bool const changed_tracks,
796 framecnt_t const drag_delta
799 RegionSelection new_views;
800 PlaylistSet modified_playlists;
801 list<RegionView*> views_to_delete;
804 /* all changes were made during motion event handlers */
806 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
810 _editor->commit_reversible_command ();
814 if (_x_constrained) {
815 _editor->begin_reversible_command (_("fixed time region copy"));
817 _editor->begin_reversible_command (_("region copy"));
820 /* insert the regions into their new playlists */
821 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
823 if (i->view->region()->locked()) {
829 if (changed_position && !_x_constrained) {
830 where = i->view->region()->position() - drag_delta;
832 where = i->view->region()->position();
835 RegionView* new_view = insert_region_into_playlist (
836 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
843 new_views.push_back (new_view);
845 /* we don't need the copied RegionView any more */
846 views_to_delete.push_back (i->view);
849 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
850 because when views are deleted they are automagically removed from _views, which messes
853 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
857 /* If we've created new regions either by copying or moving
858 to a new track, we want to replace the old selection with the new ones
861 if (new_views.size() > 0) {
862 _editor->selection->set (new_views);
865 /* write commands for the accumulated diffs for all our modified playlists */
866 add_stateful_diff_commands_for_playlists (modified_playlists);
868 _editor->commit_reversible_command ();
872 RegionMoveDrag::finished_no_copy (
873 bool const changed_position,
874 bool const changed_tracks,
875 framecnt_t const drag_delta
878 RegionSelection new_views;
879 PlaylistSet modified_playlists;
880 PlaylistSet frozen_playlists;
883 /* all changes were made during motion event handlers */
884 _editor->commit_reversible_command ();
888 if (_x_constrained) {
889 _editor->begin_reversible_command (_("fixed time region drag"));
891 _editor->begin_reversible_command (_("region drag"));
894 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
896 RegionView* rv = i->view;
898 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
899 layer_t const dest_layer = i->layer;
901 if (rv->region()->locked()) {
908 if (changed_position && !_x_constrained) {
909 where = rv->region()->position() - drag_delta;
911 where = rv->region()->position();
914 if (changed_tracks) {
916 /* insert into new playlist */
918 RegionView* new_view = insert_region_into_playlist (
919 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
927 new_views.push_back (new_view);
929 /* remove from old playlist */
931 /* the region that used to be in the old playlist is not
932 moved to the new one - we use a copy of it. as a result,
933 any existing editor for the region should no longer be
936 rv->hide_region_editor();
937 rv->fake_set_opaque (false);
939 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
943 rv->region()->clear_changes ();
946 motion on the same track. plonk the previously reparented region
947 back to its original canvas group (its streamview).
948 No need to do anything for copies as they are fake regions which will be deleted.
951 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
952 rv->get_canvas_group()->property_y() = i->initial_y;
953 rv->get_time_axis_view().reveal_dependent_views (*rv);
955 /* just change the model */
957 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
959 if (dest_rtv->view()->layer_display() == Stacked) {
960 rv->region()->set_layer (dest_layer);
961 rv->region()->set_pending_explicit_relayer (true);
964 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
966 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
972 /* this movement may result in a crossfade being modified, so we need to get undo
973 data from the playlist as well as the region.
976 r = modified_playlists.insert (playlist);
978 playlist->clear_changes ();
981 rv->region()->set_position (where, (void*) this);
983 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
986 if (changed_tracks) {
988 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
989 was selected in all of them, then removing it from a playlist will have removed all
990 trace of it from _views (i.e. there were N regions selected, we removed 1,
991 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
992 corresponding regionview, and _views is now empty).
994 This could have invalidated any and all iterators into _views.
996 The heuristic we use here is: if the region selection is empty, break out of the loop
997 here. if the region selection is not empty, then restart the loop because we know that
998 we must have removed at least the region(view) we've just been working on as well as any
999 that we processed on previous iterations.
1001 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1002 we can just iterate.
1006 if (_views.empty()) {
1017 /* If we've created new regions either by copying or moving
1018 to a new track, we want to replace the old selection with the new ones
1021 if (new_views.size() > 0) {
1022 _editor->selection->set (new_views);
1025 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1029 /* write commands for the accumulated diffs for all our modified playlists */
1030 add_stateful_diff_commands_for_playlists (modified_playlists);
1032 _editor->commit_reversible_command ();
1035 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1036 * @param region Region to remove.
1037 * @param playlist playlist To remove from.
1038 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1039 * that clear_changes () is only called once per playlist.
1042 RegionMoveDrag::remove_region_from_playlist (
1043 boost::shared_ptr<Region> region,
1044 boost::shared_ptr<Playlist> playlist,
1045 PlaylistSet& modified_playlists
1048 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1051 playlist->clear_changes ();
1054 playlist->remove_region (region);
1058 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1059 * clearing the playlist's diff history first if necessary.
1060 * @param region Region to insert.
1061 * @param dest_rtv Destination RouteTimeAxisView.
1062 * @param dest_layer Destination layer.
1063 * @param where Destination position.
1064 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1065 * that clear_changes () is only called once per playlist.
1066 * @return New RegionView, or 0 if no insert was performed.
1069 RegionMoveDrag::insert_region_into_playlist (
1070 boost::shared_ptr<Region> region,
1071 RouteTimeAxisView* dest_rtv,
1074 PlaylistSet& modified_playlists
1077 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1078 if (!dest_playlist) {
1082 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1083 _new_region_view = 0;
1084 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1086 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1087 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1089 dest_playlist->clear_changes ();
1092 dest_playlist->add_region (region, where);
1094 if (dest_rtv->view()->layer_display() == Stacked) {
1095 region->set_layer (dest_layer);
1096 region->set_pending_explicit_relayer (true);
1101 assert (_new_region_view);
1103 return _new_region_view;
1107 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1109 _new_region_view = rv;
1113 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1115 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1116 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1118 _editor->session()->add_command (new StatefulDiffCommand (*i));
1127 RegionMoveDrag::aborted ()
1131 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1138 RegionMotionDrag::aborted ();
1143 RegionMotionDrag::aborted ()
1145 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1146 RegionView* rv = i->view;
1147 TimeAxisView* tv = &(rv->get_time_axis_view ());
1148 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1150 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1151 rv->get_canvas_group()->property_y() = 0;
1152 rv->get_time_axis_view().reveal_dependent_views (*rv);
1153 rv->fake_set_opaque (false);
1154 rv->move (-_total_x_delta, 0);
1155 rv->set_height (rtv->view()->child_height ());
1158 _editor->update_canvas_now ();
1161 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1162 : RegionMotionDrag (e, i, p, v, b),
1165 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1168 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1169 if (rtv && rtv->is_track()) {
1170 speed = rtv->track()->speed ();
1173 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1177 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1179 RegionMotionDrag::start_grab (event, c);
1181 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1184 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1185 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1187 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1189 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1190 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1192 _primary = v->view()->create_region_view (r, false, false);
1194 _primary->get_canvas_group()->show ();
1195 _primary->set_position (pos, 0);
1196 _views.push_back (DraggingView (_primary, this));
1198 _last_frame_position = pos;
1200 _item = _primary->get_canvas_group ();
1204 RegionInsertDrag::finished (GdkEvent *, bool)
1206 _editor->update_canvas_now ();
1208 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1210 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1211 _primary->get_canvas_group()->property_y() = 0;
1213 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1215 _editor->begin_reversible_command (_("insert region"));
1216 playlist->clear_changes ();
1217 playlist->add_region (_primary->region (), _last_frame_position);
1218 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1219 _editor->commit_reversible_command ();
1227 RegionInsertDrag::aborted ()
1234 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1235 : RegionMoveDrag (e, i, p, v, false, false)
1237 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1240 struct RegionSelectionByPosition {
1241 bool operator() (RegionView*a, RegionView* b) {
1242 return a->region()->position () < b->region()->position();
1247 RegionSpliceDrag::motion (GdkEvent* event, bool)
1249 /* Which trackview is this ? */
1251 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1252 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1253 layer_t layer = tvp.second;
1255 if (tv && tv->layer_display() == Overlaid) {
1259 /* The region motion is only processed if the pointer is over
1263 if (!tv || !tv->is_track()) {
1264 /* To make sure we hide the verbose canvas cursor when the mouse is
1265 not held over and audiotrack.
1267 _editor->hide_verbose_canvas_cursor ();
1273 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1279 RegionSelection copy (_editor->selection->regions);
1281 RegionSelectionByPosition cmp;
1284 framepos_t const pf = adjusted_current_frame (event);
1286 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1288 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1294 boost::shared_ptr<Playlist> playlist;
1296 if ((playlist = atv->playlist()) == 0) {
1300 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1305 if (pf < (*i)->region()->last_frame() + 1) {
1309 if (pf > (*i)->region()->first_frame()) {
1315 playlist->shuffle ((*i)->region(), dir);
1320 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1322 RegionMoveDrag::finished (event, movement_occurred);
1326 RegionSpliceDrag::aborted ()
1331 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1333 _view (dynamic_cast<MidiTimeAxisView*> (v))
1335 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1341 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1347 framepos_t const f = adjusted_current_frame (event);
1348 if (f < grab_frame()) {
1349 _region->set_position (f, this);
1352 /* again, don't use a zero-length region (see above) */
1353 framecnt_t const len = abs (f - grab_frame ());
1354 _region->set_length (len < 1 ? 1 : len, this);
1360 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1362 if (!movement_occurred) {
1367 _editor->commit_reversible_command ();
1372 RegionCreateDrag::add_region ()
1374 if (_editor->session()) {
1375 const TempoMap& map (_editor->session()->tempo_map());
1376 framecnt_t pos = grab_frame();
1377 const Meter& m = map.meter_at (pos);
1378 /* not that the frame rate used here can be affected by pull up/down which
1381 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1382 _region = _view->add_region (grab_frame(), len, false);
1387 RegionCreateDrag::aborted ()
1392 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1396 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1400 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1402 Gdk::Cursor* cursor;
1403 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1405 Drag::start_grab (event);
1407 region = &cnote->region_view();
1409 double const region_start = region->get_position_pixels();
1410 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1412 if (grab_x() <= middle_point) {
1413 cursor = _editor->left_side_trim_cursor;
1416 cursor = _editor->right_side_trim_cursor;
1420 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1422 if (event->motion.state & Keyboard::PrimaryModifier) {
1428 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1430 if (ms.size() > 1) {
1431 /* has to be relative, may make no sense otherwise */
1435 /* select this note; if it is already selected, preserve the existing selection,
1436 otherwise make this note the only one selected.
1438 region->note_selected (cnote, cnote->selected ());
1440 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1441 MidiRegionSelection::iterator next;
1444 (*r)->begin_resizing (at_front);
1450 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1452 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1453 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1454 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1459 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1461 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1462 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1463 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1468 NoteResizeDrag::aborted ()
1473 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1476 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1480 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1486 RegionGainDrag::finished (GdkEvent *, bool)
1492 RegionGainDrag::aborted ()
1497 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1498 : RegionDrag (e, i, p, v)
1499 , _have_transaction (false)
1501 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1505 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1508 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1509 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1511 if (tv && tv->is_track()) {
1512 speed = tv->track()->speed();
1515 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1516 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1517 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1519 framepos_t const pf = adjusted_current_frame (event);
1521 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1522 _operation = ContentsTrim;
1523 Drag::start_grab (event, _editor->trimmer_cursor);
1525 /* These will get overridden for a point trim.*/
1526 if (pf < (region_start + region_length/2)) {
1527 /* closer to start */
1528 _operation = StartTrim;
1529 Drag::start_grab (event, _editor->left_side_trim_cursor);
1532 _operation = EndTrim;
1533 Drag::start_grab (event, _editor->right_side_trim_cursor);
1537 switch (_operation) {
1539 _editor->show_verbose_time_cursor (region_start, 10);
1542 _editor->show_verbose_time_cursor (region_end, 10);
1545 _editor->show_verbose_time_cursor (pf, 10);
1551 TrimDrag::motion (GdkEvent* event, bool first_move)
1553 RegionView* rv = _primary;
1555 /* snap modifier works differently here..
1556 its current state has to be passed to the
1557 various trim functions in order to work properly
1561 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1562 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1563 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1565 if (tv && tv->is_track()) {
1566 speed = tv->track()->speed();
1569 framepos_t const pf = adjusted_current_frame (event);
1575 switch (_operation) {
1577 trim_type = "Region start trim";
1580 trim_type = "Region end trim";
1583 trim_type = "Region content trim";
1587 _editor->begin_reversible_command (trim_type);
1588 _have_transaction = true;
1590 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1591 RegionView* rv = i->view;
1592 rv->fake_set_opaque(false);
1593 rv->enable_display (false);
1594 rv->region()->clear_changes ();
1595 rv->region()->suspend_property_changes ();
1597 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1600 arv->temporarily_hide_envelope ();
1603 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1604 insert_result = _editor->motion_frozen_playlists.insert (pl);
1606 if (insert_result.second) {
1612 bool non_overlap_trim = false;
1614 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1615 non_overlap_trim = true;
1618 switch (_operation) {
1620 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1621 _editor->single_start_trim (*i->view, pf, non_overlap_trim);
1626 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1627 _editor->single_end_trim (*i->view, pf, non_overlap_trim);
1633 bool swap_direction = false;
1635 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1636 swap_direction = true;
1639 framecnt_t frame_delta = 0;
1641 bool left_direction = false;
1642 if (last_pointer_frame() > pf) {
1643 left_direction = true;
1646 if (left_direction) {
1647 frame_delta = (last_pointer_frame() - pf);
1649 frame_delta = (pf - last_pointer_frame());
1652 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1653 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1659 switch (_operation) {
1661 _editor->show_verbose_time_cursor((framepos_t) (rv->region()->position()/speed), 10);
1664 _editor->show_verbose_time_cursor((framepos_t) (rv->region()->last_frame()/speed), 10);
1667 _editor->show_verbose_time_cursor (pf, 10);
1674 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1676 if (movement_occurred) {
1677 motion (event, false);
1679 if (!_editor->selection->selected (_primary)) {
1680 _editor->thaw_region_after_trim (*_primary);
1683 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1684 _editor->thaw_region_after_trim (*i->view);
1685 i->view->enable_display (true);
1686 i->view->fake_set_opaque (true);
1687 if (_have_transaction) {
1688 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1692 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1696 _editor->motion_frozen_playlists.clear ();
1698 if (_have_transaction) {
1699 _editor->commit_reversible_command();
1703 /* no mouse movement */
1704 _editor->point_trim (event, adjusted_current_frame (event));
1709 TrimDrag::aborted ()
1711 /* Our motion method is changing model state, so use the Undo system
1712 to cancel. Perhaps not ideal, as this will leave an Undo point
1713 behind which may be slightly odd from the user's point of view.
1718 if (_have_transaction) {
1723 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1727 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1729 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1734 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1737 // create a dummy marker for visual representation of moving the copy.
1738 // The actual copying is not done before we reach the finish callback.
1740 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1741 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1742 *new MeterSection (_marker->meter()));
1744 _item = &new_marker->the_item ();
1745 _marker = new_marker;
1749 MetricSection& section (_marker->meter());
1751 if (!section.movable()) {
1757 Drag::start_grab (event, cursor);
1759 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1761 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1765 MeterMarkerDrag::motion (GdkEvent* event, bool)
1767 framepos_t const pf = adjusted_current_frame (event);
1769 _marker->set_position (pf);
1771 _editor->show_verbose_time_cursor (pf, 10);
1775 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1777 if (!movement_occurred) {
1781 motion (event, false);
1785 TempoMap& map (_editor->session()->tempo_map());
1786 map.bbt_time (last_pointer_frame(), when);
1788 if (_copy == true) {
1789 _editor->begin_reversible_command (_("copy meter mark"));
1790 XMLNode &before = map.get_state();
1791 map.add_meter (_marker->meter(), when);
1792 XMLNode &after = map.get_state();
1793 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1794 _editor->commit_reversible_command ();
1796 // delete the dummy marker we used for visual representation of copying.
1797 // a new visual marker will show up automatically.
1800 _editor->begin_reversible_command (_("move meter mark"));
1801 XMLNode &before = map.get_state();
1802 map.move_meter (_marker->meter(), when);
1803 XMLNode &after = map.get_state();
1804 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1805 _editor->commit_reversible_command ();
1810 MeterMarkerDrag::aborted ()
1812 _marker->set_position (_marker->meter().frame ());
1815 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1819 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1821 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1826 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1831 // create a dummy marker for visual representation of moving the copy.
1832 // The actual copying is not done before we reach the finish callback.
1834 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1835 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
1836 *new TempoSection (_marker->tempo()));
1838 _item = &new_marker->the_item ();
1839 _marker = new_marker;
1843 MetricSection& section (_marker->tempo());
1845 if (!section.movable()) {
1850 Drag::start_grab (event, cursor);
1852 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1853 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1857 TempoMarkerDrag::motion (GdkEvent* event, bool)
1859 framepos_t const pf = adjusted_current_frame (event);
1860 _marker->set_position (pf);
1861 _editor->show_verbose_time_cursor (pf, 10);
1865 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1867 if (!movement_occurred) {
1871 motion (event, false);
1875 TempoMap& map (_editor->session()->tempo_map());
1876 map.bbt_time (last_pointer_frame(), when);
1878 if (_copy == true) {
1879 _editor->begin_reversible_command (_("copy tempo mark"));
1880 XMLNode &before = map.get_state();
1881 map.add_tempo (_marker->tempo(), when);
1882 XMLNode &after = map.get_state();
1883 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1884 _editor->commit_reversible_command ();
1886 // delete the dummy marker we used for visual representation of copying.
1887 // a new visual marker will show up automatically.
1890 _editor->begin_reversible_command (_("move tempo mark"));
1891 XMLNode &before = map.get_state();
1892 map.move_tempo (_marker->tempo(), when);
1893 XMLNode &after = map.get_state();
1894 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1895 _editor->commit_reversible_command ();
1900 TempoMarkerDrag::aborted ()
1902 _marker->set_position (_marker->tempo().frame());
1905 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1909 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
1911 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1916 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1918 Drag::start_grab (event, c);
1922 framepos_t where = _editor->event_frame (event, 0, 0);
1924 _editor->snap_to_with_modifier (where, event);
1925 _editor->playhead_cursor->set_position (where);
1929 if (_cursor == _editor->playhead_cursor) {
1930 _editor->_dragging_playhead = true;
1932 Session* s = _editor->session ();
1935 if (_was_rolling && _stop) {
1939 if (s->is_auditioning()) {
1940 s->cancel_audition ();
1943 s->request_suspend_timecode_transmission ();
1945 if (s->timecode_transmission_suspended ()) {
1946 framepos_t const f = _editor->playhead_cursor->current_frame;
1947 s->send_mmc_locate (f);
1948 s->send_full_time_code (f);
1953 _pointer_frame_offset = raw_grab_frame() - _cursor->current_frame;
1955 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1959 CursorDrag::motion (GdkEvent* event, bool)
1961 framepos_t const adjusted_frame = adjusted_current_frame (event);
1963 if (adjusted_frame == last_pointer_frame()) {
1967 _cursor->set_position (adjusted_frame);
1969 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1971 Session* s = _editor->session ();
1972 if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
1973 framepos_t const f = _editor->playhead_cursor->current_frame;
1974 s->send_mmc_locate (f);
1975 s->send_full_time_code (f);
1980 _editor->update_canvas_now ();
1982 _editor->UpdateAllTransportClocks (_cursor->current_frame);
1986 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
1988 _editor->_dragging_playhead = false;
1990 if (!movement_occurred && _stop) {
1994 motion (event, false);
1996 if (_item == &_editor->playhead_cursor->canvas_item) {
1997 Session* s = _editor->session ();
1999 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2000 _editor->_pending_locate_request = true;
2001 s->request_resume_timecode_transmission ();
2007 CursorDrag::aborted ()
2009 if (_editor->_dragging_playhead) {
2010 _editor->session()->request_resume_timecode_transmission ();
2011 _editor->_dragging_playhead = false;
2014 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2017 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2018 : RegionDrag (e, i, p, v)
2020 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2024 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2026 Drag::start_grab (event, cursor);
2028 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2029 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2031 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2032 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2034 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2038 FadeInDrag::motion (GdkEvent* event, bool)
2040 framecnt_t fade_length;
2042 framepos_t const pos = adjusted_current_frame (event);
2044 boost::shared_ptr<Region> region = _primary->region ();
2046 if (pos < (region->position() + 64)) {
2047 fade_length = 64; // this should be a minimum defined somewhere
2048 } else if (pos > region->last_frame()) {
2049 fade_length = region->length();
2051 fade_length = pos - region->position();
2054 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2056 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2062 tmp->reset_fade_in_shape_width (fade_length);
2063 tmp->show_fade_line((framecnt_t) fade_length);
2066 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2070 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2072 if (!movement_occurred) {
2076 framecnt_t fade_length;
2078 framepos_t const pos = adjusted_current_frame (event);
2080 boost::shared_ptr<Region> region = _primary->region ();
2082 if (pos < (region->position() + 64)) {
2083 fade_length = 64; // this should be a minimum defined somewhere
2084 } else if (pos > region->last_frame()) {
2085 fade_length = region->length();
2087 fade_length = pos - region->position();
2090 _editor->begin_reversible_command (_("change fade in length"));
2092 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2094 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2100 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2101 XMLNode &before = alist->get_state();
2103 tmp->audio_region()->set_fade_in_length (fade_length);
2104 tmp->audio_region()->set_fade_in_active (true);
2105 tmp->hide_fade_line();
2107 XMLNode &after = alist->get_state();
2108 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2111 _editor->commit_reversible_command ();
2115 FadeInDrag::aborted ()
2117 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2118 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2124 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2125 tmp->hide_fade_line();
2129 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2130 : RegionDrag (e, i, p, v)
2132 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2136 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2138 Drag::start_grab (event, cursor);
2140 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2141 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2143 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2144 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2146 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2150 FadeOutDrag::motion (GdkEvent* event, bool)
2152 framecnt_t fade_length;
2154 framepos_t const pos = adjusted_current_frame (event);
2156 boost::shared_ptr<Region> region = _primary->region ();
2158 if (pos > (region->last_frame() - 64)) {
2159 fade_length = 64; // this should really be a minimum fade defined somewhere
2161 else if (pos < region->position()) {
2162 fade_length = region->length();
2165 fade_length = region->last_frame() - pos;
2168 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2170 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2176 tmp->reset_fade_out_shape_width (fade_length);
2177 tmp->show_fade_line(region->length() - fade_length);
2180 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2184 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2186 if (!movement_occurred) {
2190 framecnt_t fade_length;
2192 framepos_t const pos = adjusted_current_frame (event);
2194 boost::shared_ptr<Region> region = _primary->region ();
2196 if (pos > (region->last_frame() - 64)) {
2197 fade_length = 64; // this should really be a minimum fade defined somewhere
2199 else if (pos < region->position()) {
2200 fade_length = region->length();
2203 fade_length = region->last_frame() - pos;
2206 _editor->begin_reversible_command (_("change fade out length"));
2208 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2210 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2216 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2217 XMLNode &before = alist->get_state();
2219 tmp->audio_region()->set_fade_out_length (fade_length);
2220 tmp->audio_region()->set_fade_out_active (true);
2221 tmp->hide_fade_line();
2223 XMLNode &after = alist->get_state();
2224 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2227 _editor->commit_reversible_command ();
2231 FadeOutDrag::aborted ()
2233 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2234 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2240 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2241 tmp->hide_fade_line();
2245 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2248 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2250 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2253 _points.push_back (Gnome::Art::Point (0, 0));
2254 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2256 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2257 _line->property_width_pixels() = 1;
2258 _line->property_points () = _points;
2261 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2264 MarkerDrag::~MarkerDrag ()
2266 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2272 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2274 Drag::start_grab (event, cursor);
2278 Location *location = _editor->find_location_from_marker (_marker, is_start);
2279 _editor->_dragging_edit_point = true;
2281 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2283 update_item (location);
2285 // _drag_line->show();
2286 // _line->raise_to_top();
2289 _editor->show_verbose_time_cursor (location->start(), 10);
2291 _editor->show_verbose_time_cursor (location->end(), 10);
2294 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2297 case Selection::Toggle:
2298 _editor->selection->toggle (_marker);
2300 case Selection::Set:
2301 if (!_editor->selection->selected (_marker)) {
2302 _editor->selection->set (_marker);
2305 case Selection::Extend:
2307 Locations::LocationList ll;
2308 list<Marker*> to_add;
2310 _editor->selection->markers.range (s, e);
2311 s = min (_marker->position(), s);
2312 e = max (_marker->position(), e);
2315 if (e < max_framepos) {
2318 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2319 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2320 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2323 to_add.push_back (lm->start);
2326 to_add.push_back (lm->end);
2330 if (!to_add.empty()) {
2331 _editor->selection->add (to_add);
2335 case Selection::Add:
2336 _editor->selection->add (_marker);
2340 /* Set up copies for us to manipulate during the drag */
2342 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2343 Location* l = _editor->find_location_from_marker (*i, is_start);
2344 _copied_locations.push_back (new Location (*l));
2349 MarkerDrag::motion (GdkEvent* event, bool)
2351 framecnt_t f_delta = 0;
2353 bool move_both = false;
2355 Location *real_location;
2356 Location *copy_location = 0;
2358 framepos_t const newframe = adjusted_current_frame (event);
2360 framepos_t next = newframe;
2362 if (newframe == last_pointer_frame()) {
2366 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2370 MarkerSelection::iterator i;
2371 list<Location*>::iterator x;
2373 /* find the marker we're dragging, and compute the delta */
2375 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2376 x != _copied_locations.end() && i != _editor->selection->markers.end();
2382 if (marker == _marker) {
2384 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2389 if (real_location->is_mark()) {
2390 f_delta = newframe - copy_location->start();
2394 switch (marker->type()) {
2396 case Marker::LoopStart:
2397 case Marker::PunchIn:
2398 f_delta = newframe - copy_location->start();
2402 case Marker::LoopEnd:
2403 case Marker::PunchOut:
2404 f_delta = newframe - copy_location->end();
2407 /* what kind of marker is this ? */
2415 if (i == _editor->selection->markers.end()) {
2416 /* hmm, impossible - we didn't find the dragged marker */
2420 /* now move them all */
2422 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2423 x != _copied_locations.end() && i != _editor->selection->markers.end();
2429 /* call this to find out if its the start or end */
2431 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2435 if (real_location->locked()) {
2439 if (copy_location->is_mark()) {
2443 copy_location->set_start (copy_location->start() + f_delta);
2447 framepos_t new_start = copy_location->start() + f_delta;
2448 framepos_t new_end = copy_location->end() + f_delta;
2450 if (is_start) { // start-of-range marker
2453 copy_location->set_start (new_start);
2454 copy_location->set_end (new_end);
2455 } else if (new_start < copy_location->end()) {
2456 copy_location->set_start (new_start);
2458 _editor->snap_to (next, 1, true);
2459 copy_location->set_end (next);
2460 copy_location->set_start (newframe);
2463 } else { // end marker
2466 copy_location->set_end (new_end);
2467 copy_location->set_start (new_start);
2468 } else if (new_end > copy_location->start()) {
2469 copy_location->set_end (new_end);
2470 } else if (newframe > 0) {
2471 _editor->snap_to (next, -1, true);
2472 copy_location->set_start (next);
2473 copy_location->set_end (newframe);
2478 update_item (copy_location);
2480 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2483 lm->set_position (copy_location->start(), copy_location->end());
2487 assert (!_copied_locations.empty());
2489 _editor->show_verbose_time_cursor (newframe, 10);
2492 _editor->update_canvas_now ();
2497 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2499 if (!movement_occurred) {
2501 /* just a click, do nothing but finish
2502 off the selection process
2505 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2508 case Selection::Set:
2509 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2510 _editor->selection->set (_marker);
2514 case Selection::Toggle:
2515 case Selection::Extend:
2516 case Selection::Add:
2523 _editor->_dragging_edit_point = false;
2525 _editor->begin_reversible_command ( _("move marker") );
2526 XMLNode &before = _editor->session()->locations()->get_state();
2528 MarkerSelection::iterator i;
2529 list<Location*>::iterator x;
2532 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2533 x != _copied_locations.end() && i != _editor->selection->markers.end();
2536 Location * location = _editor->find_location_from_marker (*i, is_start);
2540 if (location->locked()) {
2544 if (location->is_mark()) {
2545 location->set_start ((*x)->start());
2547 location->set ((*x)->start(), (*x)->end());
2552 XMLNode &after = _editor->session()->locations()->get_state();
2553 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2554 _editor->commit_reversible_command ();
2560 MarkerDrag::aborted ()
2566 MarkerDrag::update_item (Location* location)
2568 double const x1 = _editor->frame_to_pixel (location->start());
2570 _points.front().set_x(x1);
2571 _points.back().set_x(x1);
2572 _line->property_points() = _points;
2575 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2577 _cumulative_x_drag (0),
2578 _cumulative_y_drag (0)
2580 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2582 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2588 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2590 Drag::start_grab (event, _editor->fader_cursor);
2592 // start the grab at the center of the control point so
2593 // the point doesn't 'jump' to the mouse after the first drag
2594 _fixed_grab_x = _point->get_x();
2595 _fixed_grab_y = _point->get_y();
2597 float const fraction = 1 - (_point->get_y() / _point->line().height());
2599 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2601 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2602 event->button.x + 10, event->button.y + 10);
2604 _editor->show_verbose_canvas_cursor ();
2608 ControlPointDrag::motion (GdkEvent* event, bool)
2610 double dx = _drags->current_pointer_x() - last_pointer_x();
2611 double dy = _drags->current_pointer_y() - last_pointer_y();
2613 if (event->button.state & Keyboard::SecondaryModifier) {
2618 /* coordinate in pixels relative to the start of the region (for region-based automation)
2619 or track (for track-based automation) */
2620 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2621 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2623 // calculate zero crossing point. back off by .01 to stay on the
2624 // positive side of zero
2625 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2627 // make sure we hit zero when passing through
2628 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2632 if (_x_constrained) {
2635 if (_y_constrained) {
2639 _cumulative_x_drag = cx - _fixed_grab_x;
2640 _cumulative_y_drag = cy - _fixed_grab_y;
2644 cy = min ((double) _point->line().height(), cy);
2646 framepos_t cx_frames = _editor->unit_to_frame (cx);
2648 if (!_x_constrained) {
2649 _editor->snap_to_with_modifier (cx_frames, event);
2652 cx_frames = min (cx_frames, _point->line().maximum_time());
2654 float const fraction = 1.0 - (cy / _point->line().height());
2656 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2658 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2660 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2664 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2666 if (!movement_occurred) {
2670 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2671 _editor->reset_point_selection ();
2675 motion (event, false);
2678 _point->line().end_drag ();
2679 _editor->session()->commit_reversible_command ();
2683 ControlPointDrag::aborted ()
2685 _point->line().reset ();
2689 ControlPointDrag::active (Editing::MouseMode m)
2691 if (m == Editing::MouseGain) {
2692 /* always active in mouse gain */
2696 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2697 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2700 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2703 _cumulative_y_drag (0)
2705 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2709 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2711 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2714 _item = &_line->grab_item ();
2716 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2717 origin, and ditto for y.
2720 double cx = event->button.x;
2721 double cy = event->button.y;
2723 _line->parent_group().w2i (cx, cy);
2725 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2730 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2731 /* no adjacent points */
2735 Drag::start_grab (event, _editor->fader_cursor);
2737 /* store grab start in parent frame */
2742 double fraction = 1.0 - (cy / _line->height());
2744 _line->start_drag_line (before, after, fraction);
2746 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2747 event->button.x + 10, event->button.y + 10);
2749 _editor->show_verbose_canvas_cursor ();
2753 LineDrag::motion (GdkEvent* event, bool)
2755 double dy = _drags->current_pointer_y() - last_pointer_y();
2757 if (event->button.state & Keyboard::SecondaryModifier) {
2761 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2763 _cumulative_y_drag = cy - _fixed_grab_y;
2766 cy = min ((double) _line->height(), cy);
2768 double const fraction = 1.0 - (cy / _line->height());
2772 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2778 /* we are ignoring x position for this drag, so we can just pass in anything */
2779 _line->drag_motion (0, fraction, true, push);
2781 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2785 LineDrag::finished (GdkEvent* event, bool)
2787 motion (event, false);
2789 _editor->session()->commit_reversible_command ();
2793 LineDrag::aborted ()
2798 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2801 _cumulative_x_drag (0)
2803 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2807 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2809 Drag::start_grab (event);
2811 _line = reinterpret_cast<SimpleLine*> (_item);
2814 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2816 double cx = event->button.x;
2817 double cy = event->button.y;
2819 _item->property_parent().get_value()->w2i(cx, cy);
2821 /* store grab start in parent frame */
2822 _region_view_grab_x = cx;
2824 _before = _line->property_x1();
2826 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2828 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2832 FeatureLineDrag::motion (GdkEvent* event, bool)
2834 double dx = _drags->current_pointer_x() - last_pointer_x();
2836 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2838 _cumulative_x_drag += dx;
2840 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2849 _line->property_x1() = cx;
2850 _line->property_x2() = cx;
2852 _before = _line->property_x1();
2856 FeatureLineDrag::finished (GdkEvent* event, bool)
2858 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2859 _arv->update_transient(_before, _line->property_x1());
2863 FeatureLineDrag::aborted ()
2868 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2871 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2875 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2877 Drag::start_grab (event);
2878 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2882 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2889 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2891 framepos_t grab = grab_frame ();
2892 if (Config->get_rubberbanding_snaps_to_grid ()) {
2893 _editor->snap_to_with_modifier (grab, event);
2896 /* base start and end on initial click position */
2906 if (_drags->current_pointer_y() < grab_y()) {
2907 y1 = _drags->current_pointer_y();
2910 y2 = _drags->current_pointer_y();
2915 if (start != end || y1 != y2) {
2917 double x1 = _editor->frame_to_pixel (start);
2918 double x2 = _editor->frame_to_pixel (end);
2920 _editor->rubberband_rect->property_x1() = x1;
2921 _editor->rubberband_rect->property_y1() = y1;
2922 _editor->rubberband_rect->property_x2() = x2;
2923 _editor->rubberband_rect->property_y2() = y2;
2925 _editor->rubberband_rect->show();
2926 _editor->rubberband_rect->raise_to_top();
2928 _editor->show_verbose_time_cursor (pf, 10);
2933 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2935 if (movement_occurred) {
2937 motion (event, false);
2940 if (_drags->current_pointer_y() < grab_y()) {
2941 y1 = _drags->current_pointer_y();
2944 y2 = _drags->current_pointer_y();
2949 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2952 _editor->begin_reversible_command (_("rubberband selection"));
2954 if (grab_frame() < last_pointer_frame()) {
2955 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
2957 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
2961 _editor->commit_reversible_command ();
2965 if (!getenv("ARDOUR_SAE")) {
2966 _editor->selection->clear_tracks();
2968 _editor->selection->clear_regions();
2969 _editor->selection->clear_points ();
2970 _editor->selection->clear_lines ();
2973 _editor->rubberband_rect->hide();
2977 RubberbandSelectDrag::aborted ()
2979 _editor->rubberband_rect->hide ();
2982 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
2983 : RegionDrag (e, i, p, v)
2985 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
2989 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2991 Drag::start_grab (event, cursor);
2993 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2997 TimeFXDrag::motion (GdkEvent* event, bool)
2999 RegionView* rv = _primary;
3001 framepos_t const pf = adjusted_current_frame (event);
3003 if (pf > rv->region()->position()) {
3004 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3007 _editor->show_verbose_time_cursor (pf, 10);
3011 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3013 _primary->get_time_axis_view().hide_timestretch ();
3015 if (!movement_occurred) {
3019 if (last_pointer_frame() < _primary->region()->position()) {
3020 /* backwards drag of the left edge - not usable */
3024 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3026 float percentage = (double) newlen / (double) _primary->region()->length();
3028 #ifndef USE_RUBBERBAND
3029 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3030 if (_primary->region()->data_type() == DataType::AUDIO) {
3031 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3035 _editor->begin_reversible_command (_("timestretch"));
3037 // XXX how do timeFX on multiple regions ?
3042 if (_editor->time_stretch (rs, percentage) == -1) {
3043 error << _("An error occurred while executing time stretch operation") << endmsg;
3048 TimeFXDrag::aborted ()
3050 _primary->get_time_axis_view().hide_timestretch ();
3053 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3056 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3060 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3062 Drag::start_grab (event);
3066 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3068 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3072 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3074 if (movement_occurred && _editor->session()) {
3075 /* make sure we stop */
3076 _editor->session()->request_transport_speed (0.0);
3081 ScrubDrag::aborted ()
3086 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3090 , _original_pointer_time_axis (-1)
3091 , _last_pointer_time_axis (-1)
3093 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3097 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3099 framepos_t start = 0;
3102 if (_editor->session() == 0) {
3106 Gdk::Cursor* cursor = 0;
3108 switch (_operation) {
3109 case CreateSelection:
3110 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3115 cursor = _editor->selector_cursor;
3116 Drag::start_grab (event, cursor);
3119 case SelectionStartTrim:
3120 if (_editor->clicked_axisview) {
3121 _editor->clicked_axisview->order_selection_trims (_item, true);
3123 Drag::start_grab (event, _editor->left_side_trim_cursor);
3124 start = _editor->selection->time[_editor->clicked_selection].start;
3125 _pointer_frame_offset = raw_grab_frame() - start;
3128 case SelectionEndTrim:
3129 if (_editor->clicked_axisview) {
3130 _editor->clicked_axisview->order_selection_trims (_item, false);
3132 Drag::start_grab (event, _editor->right_side_trim_cursor);
3133 end = _editor->selection->time[_editor->clicked_selection].end;
3134 _pointer_frame_offset = raw_grab_frame() - end;
3138 start = _editor->selection->time[_editor->clicked_selection].start;
3139 Drag::start_grab (event, cursor);
3140 _pointer_frame_offset = raw_grab_frame() - start;
3144 if (_operation == SelectionMove) {
3145 _editor->show_verbose_time_cursor (start, 10);
3147 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3150 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3154 SelectionDrag::motion (GdkEvent* event, bool first_move)
3156 framepos_t start = 0;
3160 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3161 if (pending_time_axis.first == 0) {
3165 framepos_t const pending_position = adjusted_current_frame (event);
3167 /* only alter selection if things have changed */
3169 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3173 switch (_operation) {
3174 case CreateSelection:
3176 framepos_t grab = grab_frame ();
3179 _editor->snap_to (grab);
3182 if (pending_position < grab_frame()) {
3183 start = pending_position;
3186 end = pending_position;
3190 /* first drag: Either add to the selection
3191 or create a new selection
3197 /* adding to the selection */
3198 _editor->set_selected_track_as_side_effect (Selection::Add);
3199 //_editor->selection->add (_editor->clicked_axisview);
3200 _editor->clicked_selection = _editor->selection->add (start, end);
3205 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3206 //_editor->selection->set (_editor->clicked_axisview);
3207 _editor->set_selected_track_as_side_effect (Selection::Set);
3210 _editor->clicked_selection = _editor->selection->set (start, end);
3214 /* select the track that we're in */
3215 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3216 // _editor->set_selected_track_as_side_effect (Selection::Add);
3217 _editor->selection->add (pending_time_axis.first);
3218 _added_time_axes.push_back (pending_time_axis.first);
3221 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3222 tracks that we selected in the first place.
3225 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3226 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3228 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3229 while (i != _added_time_axes.end()) {
3231 list<TimeAxisView*>::iterator tmp = i;
3234 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3235 _editor->selection->remove (*i);
3236 _added_time_axes.remove (*i);
3245 case SelectionStartTrim:
3247 start = _editor->selection->time[_editor->clicked_selection].start;
3248 end = _editor->selection->time[_editor->clicked_selection].end;
3250 if (pending_position > end) {
3253 start = pending_position;
3257 case SelectionEndTrim:
3259 start = _editor->selection->time[_editor->clicked_selection].start;
3260 end = _editor->selection->time[_editor->clicked_selection].end;
3262 if (pending_position < start) {
3265 end = pending_position;
3272 start = _editor->selection->time[_editor->clicked_selection].start;
3273 end = _editor->selection->time[_editor->clicked_selection].end;
3275 length = end - start;
3277 start = pending_position;
3278 _editor->snap_to (start);
3280 end = start + length;
3285 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3286 _editor->start_canvas_autoscroll (1, 0);
3290 _editor->selection->replace (_editor->clicked_selection, start, end);
3293 if (_operation == SelectionMove) {
3294 _editor->show_verbose_time_cursor(start, 10);
3296 _editor->show_verbose_time_cursor(pending_position, 10);
3301 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3303 Session* s = _editor->session();
3305 if (movement_occurred) {
3306 motion (event, false);
3307 /* XXX this is not object-oriented programming at all. ick */
3308 if (_editor->selection->time.consolidate()) {
3309 _editor->selection->TimeChanged ();
3312 /* XXX what if its a music time selection? */
3313 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3314 s->request_play_range (&_editor->selection->time, true);
3319 /* just a click, no pointer movement.*/
3321 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3322 _editor->selection->clear_time();
3325 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3326 _editor->selection->set (_editor->clicked_axisview);
3329 if (s && s->get_play_range () && s->transport_rolling()) {
3330 s->request_stop (false, false);
3335 _editor->stop_canvas_autoscroll ();
3339 SelectionDrag::aborted ()
3344 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3349 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3351 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3352 physical_screen_height (_editor->get_window()));
3353 _drag_rect->hide ();
3355 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3356 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3360 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3362 if (_editor->session() == 0) {
3366 Gdk::Cursor* cursor = 0;
3368 if (!_editor->temp_location) {
3369 _editor->temp_location = new Location (*_editor->session());
3372 switch (_operation) {
3373 case CreateRangeMarker:
3374 case CreateTransportMarker:
3375 case CreateCDMarker:
3377 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3382 cursor = _editor->selector_cursor;
3386 Drag::start_grab (event, cursor);
3388 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3392 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3394 framepos_t start = 0;
3396 ArdourCanvas::SimpleRect *crect;
3398 switch (_operation) {
3399 case CreateRangeMarker:
3400 crect = _editor->range_bar_drag_rect;
3402 case CreateTransportMarker:
3403 crect = _editor->transport_bar_drag_rect;
3405 case CreateCDMarker:
3406 crect = _editor->cd_marker_bar_drag_rect;
3409 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3414 framepos_t const pf = adjusted_current_frame (event);
3416 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3417 framepos_t grab = grab_frame ();
3418 _editor->snap_to (grab);
3420 if (pf < grab_frame()) {
3428 /* first drag: Either add to the selection
3429 or create a new selection.
3434 _editor->temp_location->set (start, end);
3438 update_item (_editor->temp_location);
3440 //_drag_rect->raise_to_top();
3445 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3446 _editor->start_canvas_autoscroll (1, 0);
3450 _editor->temp_location->set (start, end);
3452 double x1 = _editor->frame_to_pixel (start);
3453 double x2 = _editor->frame_to_pixel (end);
3454 crect->property_x1() = x1;
3455 crect->property_x2() = x2;
3457 update_item (_editor->temp_location);
3460 _editor->show_verbose_time_cursor (pf, 10);
3465 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3467 Location * newloc = 0;
3471 if (movement_occurred) {
3472 motion (event, false);
3475 switch (_operation) {
3476 case CreateRangeMarker:
3477 case CreateCDMarker:
3479 _editor->begin_reversible_command (_("new range marker"));
3480 XMLNode &before = _editor->session()->locations()->get_state();
3481 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3482 if (_operation == CreateCDMarker) {
3483 flags = Location::IsRangeMarker | Location::IsCDMarker;
3484 _editor->cd_marker_bar_drag_rect->hide();
3487 flags = Location::IsRangeMarker;
3488 _editor->range_bar_drag_rect->hide();
3490 newloc = new Location (
3491 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3494 _editor->session()->locations()->add (newloc, true);
3495 XMLNode &after = _editor->session()->locations()->get_state();
3496 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3497 _editor->commit_reversible_command ();
3501 case CreateTransportMarker:
3502 // popup menu to pick loop or punch
3503 _editor->new_transport_marker_context_menu (&event->button, _item);
3507 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3509 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3514 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3516 if (end == max_framepos) {
3517 end = _editor->session()->current_end_frame ();
3520 if (start == max_framepos) {
3521 start = _editor->session()->current_start_frame ();
3524 switch (_editor->mouse_mode) {
3526 /* find the two markers on either side and then make the selection from it */
3527 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3531 /* find the two markers on either side of the click and make the range out of it */
3532 _editor->selection->set (start, end);
3541 _editor->stop_canvas_autoscroll ();
3545 RangeMarkerBarDrag::aborted ()
3551 RangeMarkerBarDrag::update_item (Location* location)
3553 double const x1 = _editor->frame_to_pixel (location->start());
3554 double const x2 = _editor->frame_to_pixel (location->end());
3556 _drag_rect->property_x1() = x1;
3557 _drag_rect->property_x2() = x2;
3560 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3563 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3567 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3569 Drag::start_grab (event, _editor->zoom_cursor);
3570 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3574 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3579 framepos_t const pf = adjusted_current_frame (event);
3581 framepos_t grab = grab_frame ();
3582 _editor->snap_to_with_modifier (grab, event);
3584 /* base start and end on initial click position */
3596 _editor->zoom_rect->show();
3597 _editor->zoom_rect->raise_to_top();
3600 _editor->reposition_zoom_rect(start, end);
3602 _editor->show_verbose_time_cursor (pf, 10);
3607 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3609 if (movement_occurred) {
3610 motion (event, false);
3612 if (grab_frame() < last_pointer_frame()) {
3613 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3615 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3618 _editor->temporal_zoom_to_frame (false, grab_frame());
3620 temporal_zoom_step (false);
3621 center_screen (grab_frame());
3625 _editor->zoom_rect->hide();
3629 MouseZoomDrag::aborted ()
3631 _editor->zoom_rect->hide ();
3634 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3636 , _cumulative_dx (0)
3637 , _cumulative_dy (0)
3639 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3641 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3642 _region = &_primary->region_view ();
3643 _note_height = _region->midi_stream_view()->note_height ();
3647 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3649 Drag::start_grab (event);
3651 if (!(_was_selected = _primary->selected())) {
3653 /* tertiary-click means extend selection - we'll do that on button release,
3654 so don't add it here, because otherwise we make it hard to figure
3655 out the "extend-to" range.
3658 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3661 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3664 _region->note_selected (_primary, true);
3666 _region->unique_select (_primary);
3672 /** @return Current total drag x change in frames */
3674 NoteDrag::total_dx () const
3677 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3679 /* primary note time */
3680 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3682 /* new time of the primary note relative to the region position */
3683 frameoffset_t const st = n + dx;
3685 /* snap and return corresponding delta */
3686 return _region->snap_frame_to_frame (st) - n;
3689 /** @return Current total drag y change in notes */
3691 NoteDrag::total_dy () const
3693 /* this is `backwards' to make increasing note number go in the right direction */
3694 double const dy = _drags->current_pointer_y() - grab_y();
3699 if (abs (dy) >= _note_height) {
3701 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3703 ndy = (int8_t) floor (dy / _note_height / 2.0);
3707 /* more positive value = higher pitch and higher y-axis position on track,
3708 which is the inverse of the X-centric geometric universe
3715 NoteDrag::motion (GdkEvent *, bool)
3717 /* Total change in x and y since the start of the drag */
3718 frameoffset_t const dx = total_dx ();
3719 int8_t const dy = -total_dy ();
3721 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3722 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3723 double const tdy = dy * _note_height - _cumulative_dy;
3726 _cumulative_dx += tdx;
3727 _cumulative_dy += tdy;
3729 int8_t note_delta = total_dy();
3731 _region->move_selection (tdx, tdy, note_delta);
3734 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3735 (int) floor (_primary->note()->note() + note_delta));
3737 _editor->show_verbose_canvas_cursor_with (buf);
3742 NoteDrag::finished (GdkEvent* ev, bool moved)
3745 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3747 if (_was_selected) {
3748 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3750 _region->note_deselected (_primary);
3753 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3754 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3756 if (!extend && !add && _region->selection_size() > 1) {
3757 _region->unique_select (_primary);
3758 } else if (extend) {
3759 _region->note_selected (_primary, true, true);
3761 /* it was added during button press */
3766 _region->note_dropped (_primary, total_dx(), - total_dy());
3771 NoteDrag::aborted ()
3776 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3777 : Drag (editor, item)
3779 , _nothing_to_drag (false)
3781 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3783 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3786 /* get all lines in the automation view */
3787 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3789 /* find those that overlap the ranges being dragged */
3790 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3791 while (i != lines.end ()) {
3792 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3795 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3797 /* check this range against all the AudioRanges that we are using */
3798 list<AudioRange>::const_iterator k = _ranges.begin ();
3799 while (k != _ranges.end()) {
3800 if (k->coverage (r.first, r.second) != OverlapNone) {
3806 /* add it to our list if it overlaps at all */
3807 if (k != _ranges.end()) {
3812 _lines.push_back (n);
3818 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3822 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3824 Drag::start_grab (event, cursor);
3826 /* Get line states before we start changing things */
3827 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3828 i->state = &i->line->get_state ();
3831 if (_ranges.empty()) {
3833 /* No selected time ranges: drag all points */
3834 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3835 uint32_t const N = i->line->npoints ();
3836 for (uint32_t j = 0; j < N; ++j) {
3837 i->points.push_back (i->line->nth (j));
3843 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3845 framecnt_t const half = (i->start + i->end) / 2;
3847 /* find the line that this audio range starts in */
3848 list<Line>::iterator j = _lines.begin();
3849 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3853 if (j != _lines.end()) {
3854 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3856 /* j is the line that this audio range starts in; fade into it;
3857 64 samples length plucked out of thin air.
3860 framepos_t a = i->start + 64;
3865 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3866 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3868 the_list->add (p, the_list->eval (p));
3869 j->line->add_always_in_view (p);
3870 the_list->add (q, the_list->eval (q));
3871 j->line->add_always_in_view (q);
3874 /* same thing for the end */
3877 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3881 if (j != _lines.end()) {
3882 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3884 /* j is the line that this audio range starts in; fade out of it;
3885 64 samples length plucked out of thin air.
3888 framepos_t b = i->end - 64;
3893 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3894 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
3896 the_list->add (p, the_list->eval (p));
3897 j->line->add_always_in_view (p);
3898 the_list->add (q, the_list->eval (q));
3899 j->line->add_always_in_view (q);
3903 _nothing_to_drag = true;
3905 /* Find all the points that should be dragged and put them in the relevant
3906 points lists in the Line structs.
3909 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3911 uint32_t const N = i->line->npoints ();
3912 for (uint32_t j = 0; j < N; ++j) {
3914 /* here's a control point on this line */
3915 ControlPoint* p = i->line->nth (j);
3916 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
3918 /* see if it's inside a range */
3919 list<AudioRange>::const_iterator k = _ranges.begin ();
3920 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
3924 if (k != _ranges.end()) {
3925 /* dragging this point */
3926 _nothing_to_drag = false;
3927 i->points.push_back (p);
3933 if (_nothing_to_drag) {
3937 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3938 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
3943 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3945 if (_nothing_to_drag) {
3949 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3950 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
3952 /* we are ignoring x position for this drag, so we can just pass in anything */
3953 i->line->drag_motion (0, f, true, false);
3958 AutomationRangeDrag::finished (GdkEvent* event, bool)
3960 if (_nothing_to_drag) {
3964 motion (event, false);
3965 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3966 i->line->end_drag ();
3967 i->line->clear_always_in_view ();
3970 _editor->session()->commit_reversible_command ();
3974 AutomationRangeDrag::aborted ()
3976 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3977 i->line->clear_always_in_view ();
3982 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
3985 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
3986 layer = v->region()->layer ();
3987 initial_y = v->get_canvas_group()->property_y ();
3988 initial_playlist = v->region()->playlist ();