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"
47 using namespace ARDOUR;
50 using namespace Editing;
51 using namespace ArdourCanvas;
53 using Gtkmm2ext::Keyboard;
55 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
57 DragManager::DragManager (Editor* e)
60 , _current_pointer_frame (0)
65 DragManager::~DragManager ()
70 /** Call abort for each active drag */
76 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
87 DragManager::add (Drag* d)
89 d->set_manager (this);
94 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
96 assert (_drags.empty ());
97 d->set_manager (this);
103 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
105 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
107 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
108 (*i)->start_grab (e, c);
112 /** Call end_grab for each active drag.
113 * @return true if any drag reported movement having occurred.
116 DragManager::end_grab (GdkEvent* e)
121 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
122 bool const t = (*i)->end_grab (e);
137 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
141 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
143 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
144 bool const t = (*i)->motion_handler (e, from_autoscroll);
155 DragManager::have_item (ArdourCanvas::Item* i) const
157 list<Drag*>::const_iterator j = _drags.begin ();
158 while (j != _drags.end() && (*j)->item () != i) {
162 return j != _drags.end ();
165 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
168 , _pointer_frame_offset (0)
169 , _move_threshold_passed (false)
171 , _last_pointer_frame (0)
177 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
183 cursor = _editor->which_grabber_cursor ();
186 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
190 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
193 cursor = _editor->which_grabber_cursor ();
196 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
198 if (Keyboard::is_button2_event (&event->button)) {
199 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
200 _y_constrained = true;
201 _x_constrained = false;
203 _y_constrained = false;
204 _x_constrained = true;
207 _x_constrained = false;
208 _y_constrained = false;
211 _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
212 _grab_frame = adjusted_frame (_grab_frame, event);
213 _last_pointer_frame = _grab_frame;
214 _last_pointer_x = _grab_x;
215 _last_pointer_y = _grab_y;
217 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
221 if (_editor->session() && _editor->session()->transport_rolling()) {
224 _was_rolling = false;
227 switch (_editor->snap_type()) {
228 case SnapToRegionStart:
229 case SnapToRegionEnd:
230 case SnapToRegionSync:
231 case SnapToRegionBoundary:
232 _editor->build_region_boundary_cache ();
239 /** Call to end a drag `successfully'. Ungrabs item and calls
240 * subclass' finished() method.
242 * @param event GDK event, or 0.
243 * @return true if some movement occurred, otherwise false.
246 Drag::end_grab (GdkEvent* event)
248 _editor->stop_canvas_autoscroll ();
250 _item->ungrab (event ? event->button.time : 0);
252 finished (event, _move_threshold_passed);
254 _editor->hide_verbose_canvas_cursor();
256 return _move_threshold_passed;
260 Drag::adjusted_frame (nframes64_t f, GdkEvent const * event, bool snap) const
264 if (f > _pointer_frame_offset) {
265 pos = f - _pointer_frame_offset;
269 _editor->snap_to_with_modifier (pos, event);
276 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
278 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
282 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
284 /* check to see if we have moved in any way that matters since the last motion event */
285 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
286 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
290 pair<nframes64_t, int> const threshold = move_threshold ();
292 bool const old_move_threshold_passed = _move_threshold_passed;
294 if (!from_autoscroll && !_move_threshold_passed) {
296 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
297 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
299 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
302 if (active (_editor->mouse_mode) && _move_threshold_passed) {
304 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
305 if (!from_autoscroll) {
306 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
309 motion (event, _move_threshold_passed != old_move_threshold_passed);
311 _last_pointer_x = _drags->current_pointer_x ();
312 _last_pointer_y = _drags->current_pointer_y ();
313 _last_pointer_frame = adjusted_current_frame (event);
321 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
331 _editor->stop_canvas_autoscroll ();
332 _editor->hide_verbose_canvas_cursor ();
335 struct EditorOrderTimeAxisViewSorter {
336 bool operator() (TimeAxisView* a, TimeAxisView* b) {
337 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
338 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
340 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
344 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
348 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
350 /* Make a list of non-hidden tracks to refer to during the drag */
352 TrackViewList track_views = _editor->track_views;
353 track_views.sort (EditorOrderTimeAxisViewSorter ());
355 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
356 if (!(*i)->hidden()) {
358 _time_axis_views.push_back (*i);
360 TimeAxisView::Children children_list = (*i)->get_child_list ();
361 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
362 _time_axis_views.push_back (j->get());
367 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
368 _views.push_back (DraggingView (*i, this));
371 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
375 RegionDrag::region_going_away (RegionView* v)
377 list<DraggingView>::iterator i = _views.begin ();
378 while (i != _views.end() && i->view != v) {
382 if (i != _views.end()) {
387 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
389 RegionDrag::find_time_axis_view (TimeAxisView* t) const
392 int const N = _time_axis_views.size ();
393 while (i < N && _time_axis_views[i] != t) {
404 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
405 : RegionDrag (e, i, p, v),
414 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
416 Drag::start_grab (event, cursor);
418 _editor->show_verbose_time_cursor (_last_frame_position, 10);
420 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
421 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
422 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
426 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
428 /* compute the amount of pointer motion in frames, and where
429 the region would be if we moved it by that much.
431 *pending_region_position = adjusted_current_frame (event);
433 nframes64_t sync_frame;
434 nframes64_t sync_offset;
437 sync_offset = _primary->region()->sync_offset (sync_dir);
439 /* we don't handle a sync point that lies before zero.
441 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
443 sync_frame = *pending_region_position + (sync_dir*sync_offset);
445 _editor->snap_to_with_modifier (sync_frame, event);
447 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
450 *pending_region_position = _last_frame_position;
453 if (*pending_region_position > max_frames - _primary->region()->length()) {
454 *pending_region_position = _last_frame_position;
459 /* in locked edit mode, reverse the usual meaning of _x_constrained */
460 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
462 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
464 /* x movement since last time */
465 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
467 /* total x movement */
468 framecnt_t total_dx = *pending_region_position;
469 if (regions_came_from_canvas()) {
470 total_dx = total_dx - grab_frame () + _pointer_frame_offset;
473 /* check that no regions have gone off the start of the session */
474 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
475 if ((i->view->region()->position() + total_dx) < 0) {
477 *pending_region_position = _last_frame_position;
482 _last_frame_position = *pending_region_position;
489 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
491 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
492 int const n = i->time_axis_view + delta_track;
493 if (n < 0 || n >= int (_time_axis_views.size())) {
494 /* off the top or bottom track */
498 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
499 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
500 /* not a track, or the wrong type */
504 int const l = i->layer + delta_layer;
505 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
506 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
507 If it has, the layers will be munged later anyway, so it's ok.
513 /* all regions being dragged are ok with this change */
518 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
520 /* Find the TimeAxisView that the pointer is now over */
521 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
523 /* Bail early if we're not over a track */
524 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
525 if (!rtv || !rtv->is_track()) {
526 _editor->hide_verbose_canvas_cursor ();
530 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
532 /* Here's the current pointer position in terms of time axis view and layer */
533 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
534 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
536 /* Work out the change in x */
537 framepos_t pending_region_position;
538 double const x_delta = compute_x_delta (event, &pending_region_position);
540 /* Work out the change in y */
541 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
542 int delta_layer = current_pointer_layer - _last_pointer_layer;
544 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
545 /* this y movement is not allowed, so do no y movement this time */
546 delta_time_axis_view = 0;
550 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
551 /* haven't reached next snap point, and we're not switching
552 trackviews nor layers. nothing to do.
557 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
559 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
561 RegionView* rv = i->view;
563 if (rv->region()->locked()) {
569 /* here we are calculating the y distance from the
570 top of the first track view to the top of the region
571 area of the track view that we're working on */
573 /* this x value is just a dummy value so that we have something
578 /* distance from the top of this track view to the region area
579 of our track view is always 1 */
583 /* convert to world coordinates, ie distance from the top of
586 rv->get_canvas_frame()->i2w (ix1, iy1);
588 /* compensate for the ruler section and the vertical scrollbar position */
589 iy1 += _editor->get_trackview_group_vertical_offset ();
591 // hide any dependent views
593 rv->get_time_axis_view().hide_dependent_views (*rv);
596 reparent to a non scrolling group so that we can keep the
597 region selection above all time axis views.
598 reparenting means we have to move the rv as the two
599 parent groups have different coordinates.
602 rv->get_canvas_group()->property_y() = iy1 - 1;
603 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
605 rv->fake_set_opaque (true);
608 /* Work out the change in y position of this region view */
612 /* If we have moved tracks, we'll fudge the layer delta so that the
613 region gets moved back onto layer 0 on its new track; this avoids
614 confusion when dragging regions from non-zero layers onto different
617 int this_delta_layer = delta_layer;
618 if (delta_time_axis_view != 0) {
619 this_delta_layer = - i->layer;
622 /* Move this region to layer 0 on its old track */
623 StreamView* lv = _time_axis_views[i->time_axis_view]->view ();
624 if (lv->layer_display() == Stacked) {
625 y_delta -= (lv->layers() - i->layer - 1) * lv->child_height ();
628 /* Now move it to its right layer on the current track */
629 StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view ();
630 if (cv->layer_display() == Stacked) {
631 y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height ();
635 if (delta_time_axis_view > 0) {
636 for (int j = 0; j < delta_time_axis_view; ++j) {
637 y_delta += _time_axis_views[i->time_axis_view + j]->current_height ();
640 /* start by subtracting the height of the track above where we are now */
641 for (int j = 1; j <= -delta_time_axis_view; ++j) {
642 y_delta -= _time_axis_views[i->time_axis_view - j]->current_height ();
647 rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ());
649 /* Update the DraggingView */
650 i->time_axis_view += delta_time_axis_view;
651 i->layer += this_delta_layer;
654 _editor->mouse_brush_insert_region (rv, pending_region_position);
656 rv->move (x_delta, y_delta);
659 } /* foreach region */
661 _total_x_delta += x_delta;
664 _editor->cursor_group->raise_to_top();
667 if (x_delta != 0 && !_brushing) {
668 _editor->show_verbose_time_cursor (_last_frame_position, 10);
671 _last_pointer_time_axis_view += delta_time_axis_view;
672 _last_pointer_layer += delta_layer;
676 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
678 if (_copy && first_move) {
680 /* duplicate the regionview(s) and region(s) */
682 list<DraggingView> new_regionviews;
684 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
686 RegionView* rv = i->view;
687 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
688 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
690 const boost::shared_ptr<const Region> original = rv->region();
691 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
692 region_copy->set_position (original->position(), this);
696 boost::shared_ptr<AudioRegion> audioregion_copy
697 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
699 nrv = new AudioRegionView (*arv, audioregion_copy);
701 boost::shared_ptr<MidiRegion> midiregion_copy
702 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
703 nrv = new MidiRegionView (*mrv, midiregion_copy);
708 nrv->get_canvas_group()->show ();
709 new_regionviews.push_back (DraggingView (nrv, this));
711 /* swap _primary to the copy */
713 if (rv == _primary) {
717 /* ..and deselect the one we copied */
719 rv->set_selected (false);
722 if (!new_regionviews.empty()) {
724 /* reflect the fact that we are dragging the copies */
726 _views = new_regionviews;
728 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
731 sync the canvas to what we think is its current state
732 without it, the canvas seems to
733 "forget" to update properly after the upcoming reparent()
734 ..only if the mouse is in rapid motion at the time of the grab.
735 something to do with regionview creation taking so long?
737 _editor->update_canvas_now();
741 RegionMotionDrag::motion (event, first_move);
745 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
747 if (!movement_occurred) {
752 /* reverse this here so that we have the correct logic to finalize
756 if (Config->get_edit_mode() == Lock) {
757 _x_constrained = !_x_constrained;
760 bool const changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
761 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
762 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
764 _editor->update_canvas_now ();
786 RegionMoveDrag::finished_copy (
787 bool const changed_position,
788 bool const changed_tracks,
789 framecnt_t const drag_delta
792 RegionSelection new_views;
793 PlaylistSet modified_playlists;
794 list<RegionView*> views_to_delete;
797 /* all changes were made during motion event handlers */
799 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
803 _editor->commit_reversible_command ();
807 if (_x_constrained) {
808 _editor->begin_reversible_command (_("fixed time region copy"));
810 _editor->begin_reversible_command (_("region copy"));
813 /* insert the regions into their new playlists */
814 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
816 if (i->view->region()->locked()) {
822 if (changed_position && !_x_constrained) {
823 where = i->view->region()->position() - drag_delta;
825 where = i->view->region()->position();
828 RegionView* new_view = insert_region_into_playlist (
829 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
836 new_views.push_back (new_view);
838 /* we don't need the copied RegionView any more */
839 views_to_delete.push_back (i->view);
842 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
843 because when views are deleted they are automagically removed from _views, which messes
846 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
850 /* If we've created new regions either by copying or moving
851 to a new track, we want to replace the old selection with the new ones
854 if (new_views.size() > 0) {
855 _editor->selection->set (new_views);
858 /* write commands for the accumulated diffs for all our modified playlists */
859 add_stateful_diff_commands_for_playlists (modified_playlists);
861 _editor->commit_reversible_command ();
865 RegionMoveDrag::finished_no_copy (
866 bool const changed_position,
867 bool const changed_tracks,
868 framecnt_t const drag_delta
871 RegionSelection new_views;
872 PlaylistSet modified_playlists;
873 PlaylistSet frozen_playlists;
876 /* all changes were made during motion event handlers */
877 _editor->commit_reversible_command ();
881 if (_x_constrained) {
882 _editor->begin_reversible_command (_("fixed time region drag"));
884 _editor->begin_reversible_command (_("region drag"));
887 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
889 RegionView* rv = i->view;
891 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
892 layer_t const dest_layer = i->layer;
894 if (rv->region()->locked()) {
901 if (changed_position && !_x_constrained) {
902 where = rv->region()->position() - drag_delta;
904 where = rv->region()->position();
907 if (changed_tracks) {
909 /* insert into new playlist */
911 RegionView* new_view = insert_region_into_playlist (
912 RegionFactory::create (rv->region ()), dest_rtv, dest_layer, where, modified_playlists
920 new_views.push_back (new_view);
922 /* remove from old playlist */
924 /* the region that used to be in the old playlist is not
925 moved to the new one - we use a copy of it. as a result,
926 any existing editor for the region should no longer be
929 rv->hide_region_editor();
930 rv->fake_set_opaque (false);
932 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
936 rv->region()->clear_changes ();
939 motion on the same track. plonk the previously reparented region
940 back to its original canvas group (its streamview).
941 No need to do anything for copies as they are fake regions which will be deleted.
944 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
945 rv->get_canvas_group()->property_y() = i->initial_y;
946 rv->get_time_axis_view().reveal_dependent_views (*rv);
948 /* just change the model */
950 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
952 if (dest_rtv->view()->layer_display() == Stacked) {
953 rv->region()->set_layer (dest_layer);
954 rv->region()->set_pending_explicit_relayer (true);
957 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
959 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
965 /* this movement may result in a crossfade being modified, so we need to get undo
966 data from the playlist as well as the region.
969 r = modified_playlists.insert (playlist);
971 playlist->clear_changes ();
974 rv->region()->set_position (where, (void*) this);
976 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
979 if (changed_tracks) {
981 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
982 was selected in all of them, then removing it from a playlist will have removed all
983 trace of it from _views (i.e. there were N regions selected, we removed 1,
984 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
985 corresponding regionview, and _views is now empty).
987 This could have invalidated any and all iterators into _views.
989 The heuristic we use here is: if the region selection is empty, break out of the loop
990 here. if the region selection is not empty, then restart the loop because we know that
991 we must have removed at least the region(view) we've just been working on as well as any
992 that we processed on previous iterations.
994 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
999 if (_views.empty()) {
1010 /* If we've created new regions either by copying or moving
1011 to a new track, we want to replace the old selection with the new ones
1014 if (new_views.size() > 0) {
1015 _editor->selection->set (new_views);
1018 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1022 /* write commands for the accumulated diffs for all our modified playlists */
1023 add_stateful_diff_commands_for_playlists (modified_playlists);
1025 _editor->commit_reversible_command ();
1028 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1029 * @param region Region to remove.
1030 * @param playlist playlist To remove from.
1031 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1032 * that clear_changes () is only called once per playlist.
1035 RegionMoveDrag::remove_region_from_playlist (
1036 boost::shared_ptr<Region> region,
1037 boost::shared_ptr<Playlist> playlist,
1038 PlaylistSet& modified_playlists
1041 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1044 playlist->clear_changes ();
1047 playlist->remove_region (region);
1051 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1052 * clearing the playlist's diff history first if necessary.
1053 * @param region Region to insert.
1054 * @param dest_rtv Destination RouteTimeAxisView.
1055 * @param dest_layer Destination layer.
1056 * @param where Destination position.
1057 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1058 * that clear_changes () is only called once per playlist.
1059 * @return New RegionView, or 0 if no insert was performed.
1062 RegionMoveDrag::insert_region_into_playlist (
1063 boost::shared_ptr<Region> region,
1064 RouteTimeAxisView* dest_rtv,
1067 PlaylistSet& modified_playlists
1070 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1071 if (!dest_playlist) {
1075 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1076 _new_region_view = 0;
1077 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1079 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1080 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1082 dest_playlist->clear_changes ();
1085 dest_playlist->add_region (region, where);
1087 if (dest_rtv->view()->layer_display() == Stacked) {
1088 region->set_layer (dest_layer);
1089 region->set_pending_explicit_relayer (true);
1094 assert (_new_region_view);
1096 return _new_region_view;
1100 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1102 _new_region_view = rv;
1106 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1108 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1109 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1111 _editor->session()->add_command (new StatefulDiffCommand (*i));
1120 RegionMoveDrag::aborted ()
1124 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1131 RegionMotionDrag::aborted ();
1136 RegionMotionDrag::aborted ()
1138 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1139 RegionView* rv = i->view;
1140 TimeAxisView* tv = &(rv->get_time_axis_view ());
1141 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1143 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1144 rv->get_canvas_group()->property_y() = 0;
1145 rv->get_time_axis_view().reveal_dependent_views (*rv);
1146 rv->fake_set_opaque (false);
1147 rv->move (-_total_x_delta, 0);
1148 rv->set_height (rtv->view()->child_height ());
1151 _editor->update_canvas_now ();
1154 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1155 : RegionMotionDrag (e, i, p, v, b),
1159 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1160 if (rtv && rtv->is_track()) {
1161 speed = rtv->track()->speed ();
1164 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1168 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1170 RegionMotionDrag::start_grab (event, c);
1172 _pointer_frame_offset = grab_frame() - _last_frame_position;
1175 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1176 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1178 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1179 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1181 _primary = v->view()->create_region_view (r, false, false);
1183 _primary->get_canvas_group()->show ();
1184 _primary->set_position (pos, 0);
1185 _views.push_back (DraggingView (_primary, this));
1187 _last_frame_position = pos;
1189 _item = _primary->get_canvas_group ();
1193 RegionInsertDrag::finished (GdkEvent *, bool)
1195 _editor->update_canvas_now ();
1197 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1199 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1200 _primary->get_canvas_group()->property_y() = 0;
1202 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1204 _editor->begin_reversible_command (_("insert region"));
1205 playlist->clear_changes ();
1206 playlist->add_region (_primary->region (), _last_frame_position);
1207 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1208 _editor->commit_reversible_command ();
1216 RegionInsertDrag::aborted ()
1223 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1224 : RegionMoveDrag (e, i, p, v, false, false)
1229 struct RegionSelectionByPosition {
1230 bool operator() (RegionView*a, RegionView* b) {
1231 return a->region()->position () < b->region()->position();
1236 RegionSpliceDrag::motion (GdkEvent* event, bool)
1238 /* Which trackview is this ? */
1240 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1241 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1242 layer_t layer = tvp.second;
1244 if (tv && tv->layer_display() == Overlaid) {
1248 /* The region motion is only processed if the pointer is over
1252 if (!tv || !tv->is_track()) {
1253 /* To make sure we hide the verbose canvas cursor when the mouse is
1254 not held over and audiotrack.
1256 _editor->hide_verbose_canvas_cursor ();
1262 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1268 RegionSelection copy (_editor->selection->regions);
1270 RegionSelectionByPosition cmp;
1273 nframes64_t const pf = adjusted_current_frame (event);
1275 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1277 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1283 boost::shared_ptr<Playlist> playlist;
1285 if ((playlist = atv->playlist()) == 0) {
1289 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1294 if (pf < (*i)->region()->last_frame() + 1) {
1298 if (pf > (*i)->region()->first_frame()) {
1304 playlist->shuffle ((*i)->region(), dir);
1309 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1311 RegionMoveDrag::finished (event, movement_occurred);
1315 RegionSpliceDrag::aborted ()
1320 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1322 _view (dynamic_cast<MidiTimeAxisView*> (v))
1328 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1331 /* don't use a zero-length region otherwise its region view will be hidden when it is created */
1332 _region = _view->add_region (grab_frame(), 1, false);
1334 framepos_t const f = adjusted_current_frame (event);
1335 if (f < grab_frame()) {
1336 _region->set_position (f, this);
1339 /* again, don't use a zero-length region (see above) */
1340 framecnt_t const len = abs (f - grab_frame ());
1341 _region->set_length (len < 1 ? 1 : len, this);
1346 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1348 if (movement_occurred) {
1349 _editor->commit_reversible_command ();
1354 RegionCreateDrag::aborted ()
1359 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1367 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1369 Gdk::Cursor* cursor;
1370 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1372 Drag::start_grab (event);
1374 region = &cnote->region_view();
1376 double const region_start = region->get_position_pixels();
1377 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1379 if (grab_x() <= middle_point) {
1380 cursor = _editor->left_side_trim_cursor;
1383 cursor = _editor->right_side_trim_cursor;
1387 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1389 if (event->motion.state & Keyboard::PrimaryModifier) {
1395 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1397 if (ms.size() > 1) {
1398 /* has to be relative, may make no sense otherwise */
1402 /* select this note; if it is already selected, preserve the existing selection,
1403 otherwise make this note the only one selected.
1405 region->note_selected (cnote, cnote->selected ());
1407 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1408 MidiRegionSelection::iterator next;
1411 (*r)->begin_resizing (at_front);
1417 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1419 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1420 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1421 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1426 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1428 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1429 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1430 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1435 NoteResizeDrag::aborted ()
1441 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1447 RegionGainDrag::finished (GdkEvent *, bool)
1453 RegionGainDrag::aborted ()
1458 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1459 : RegionDrag (e, i, p, v)
1460 , _have_transaction (false)
1466 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1469 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1470 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1472 if (tv && tv->is_track()) {
1473 speed = tv->track()->speed();
1476 nframes64_t const region_start = (nframes64_t) (_primary->region()->position() / speed);
1477 nframes64_t const region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1478 nframes64_t const region_length = (nframes64_t) (_primary->region()->length() / speed);
1480 nframes64_t const pf = adjusted_current_frame (event);
1482 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1483 _operation = ContentsTrim;
1484 Drag::start_grab (event, _editor->trimmer_cursor);
1486 /* These will get overridden for a point trim.*/
1487 if (pf < (region_start + region_length/2)) {
1488 /* closer to start */
1489 _operation = StartTrim;
1490 Drag::start_grab (event, _editor->left_side_trim_cursor);
1493 _operation = EndTrim;
1494 Drag::start_grab (event, _editor->right_side_trim_cursor);
1498 switch (_operation) {
1500 _editor->show_verbose_time_cursor (region_start, 10);
1503 _editor->show_verbose_time_cursor (region_end, 10);
1506 _editor->show_verbose_time_cursor (pf, 10);
1512 TrimDrag::motion (GdkEvent* event, bool first_move)
1514 RegionView* rv = _primary;
1516 /* snap modifier works differently here..
1517 its current state has to be passed to the
1518 various trim functions in order to work properly
1522 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1523 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1524 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1526 if (tv && tv->is_track()) {
1527 speed = tv->track()->speed();
1530 nframes64_t const pf = adjusted_current_frame (event);
1536 switch (_operation) {
1538 trim_type = "Region start trim";
1541 trim_type = "Region end trim";
1544 trim_type = "Region content trim";
1548 _editor->begin_reversible_command (trim_type);
1549 _have_transaction = true;
1551 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1552 RegionView* rv = i->view;
1553 rv->fake_set_opaque(false);
1554 rv->enable_display (false);
1555 rv->region()->clear_changes ();
1556 rv->region()->suspend_property_changes ();
1558 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1561 arv->temporarily_hide_envelope ();
1564 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1565 insert_result = _editor->motion_frozen_playlists.insert (pl);
1567 if (insert_result.second) {
1573 bool non_overlap_trim = false;
1575 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1576 non_overlap_trim = true;
1579 switch (_operation) {
1581 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1582 _editor->single_start_trim (*i->view, pf, non_overlap_trim);
1587 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1588 _editor->single_end_trim (*i->view, pf, non_overlap_trim);
1594 bool swap_direction = false;
1596 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1597 swap_direction = true;
1600 nframes64_t frame_delta = 0;
1602 bool left_direction = false;
1603 if (last_pointer_frame() > pf) {
1604 left_direction = true;
1607 if (left_direction) {
1608 frame_delta = (last_pointer_frame() - pf);
1610 frame_delta = (pf - last_pointer_frame());
1613 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1614 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1620 switch (_operation) {
1622 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1625 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1628 _editor->show_verbose_time_cursor (pf, 10);
1635 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1637 if (movement_occurred) {
1638 motion (event, false);
1640 if (!_editor->selection->selected (_primary)) {
1641 _editor->thaw_region_after_trim (*_primary);
1644 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1645 _editor->thaw_region_after_trim (*i->view);
1646 i->view->enable_display (true);
1647 i->view->fake_set_opaque (true);
1648 if (_have_transaction) {
1649 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1653 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1657 _editor->motion_frozen_playlists.clear ();
1659 if (_have_transaction) {
1660 _editor->commit_reversible_command();
1664 /* no mouse movement */
1665 _editor->point_trim (event, adjusted_current_frame (event));
1670 TrimDrag::aborted ()
1672 /* Our motion method is changing model state, so use the Undo system
1673 to cancel. Perhaps not ideal, as this will leave an Undo point
1674 behind which may be slightly odd from the user's point of view.
1679 if (_have_transaction) {
1684 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1688 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1693 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1696 // create a dummy marker for visual representation of moving the copy.
1697 // The actual copying is not done before we reach the finish callback.
1699 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1700 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1701 *new MeterSection (_marker->meter()));
1703 _item = &new_marker->the_item ();
1704 _marker = new_marker;
1708 MetricSection& section (_marker->meter());
1710 if (!section.movable()) {
1716 Drag::start_grab (event, cursor);
1718 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1720 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1724 MeterMarkerDrag::motion (GdkEvent* event, bool)
1726 nframes64_t const pf = adjusted_current_frame (event);
1728 _marker->set_position (pf);
1730 _editor->show_verbose_time_cursor (pf, 10);
1734 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1736 if (!movement_occurred) {
1740 motion (event, false);
1744 TempoMap& map (_editor->session()->tempo_map());
1745 map.bbt_time (last_pointer_frame(), when);
1747 if (_copy == true) {
1748 _editor->begin_reversible_command (_("copy meter mark"));
1749 XMLNode &before = map.get_state();
1750 map.add_meter (_marker->meter(), when);
1751 XMLNode &after = map.get_state();
1752 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1753 _editor->commit_reversible_command ();
1755 // delete the dummy marker we used for visual representation of copying.
1756 // a new visual marker will show up automatically.
1759 _editor->begin_reversible_command (_("move meter mark"));
1760 XMLNode &before = map.get_state();
1761 map.move_meter (_marker->meter(), when);
1762 XMLNode &after = map.get_state();
1763 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1764 _editor->commit_reversible_command ();
1769 MeterMarkerDrag::aborted ()
1771 _marker->set_position (_marker->meter().frame ());
1774 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1778 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1783 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1788 // create a dummy marker for visual representation of moving the copy.
1789 // The actual copying is not done before we reach the finish callback.
1791 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1792 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
1793 *new TempoSection (_marker->tempo()));
1795 _item = &new_marker->the_item ();
1796 _marker = new_marker;
1800 MetricSection& section (_marker->tempo());
1802 if (!section.movable()) {
1807 Drag::start_grab (event, cursor);
1809 _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
1810 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1814 TempoMarkerDrag::motion (GdkEvent* event, bool)
1816 nframes64_t const pf = adjusted_current_frame (event);
1817 _marker->set_position (pf);
1818 _editor->show_verbose_time_cursor (pf, 10);
1822 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1824 if (!movement_occurred) {
1828 motion (event, false);
1832 TempoMap& map (_editor->session()->tempo_map());
1833 map.bbt_time (last_pointer_frame(), when);
1835 if (_copy == true) {
1836 _editor->begin_reversible_command (_("copy tempo mark"));
1837 XMLNode &before = map.get_state();
1838 map.add_tempo (_marker->tempo(), when);
1839 XMLNode &after = map.get_state();
1840 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1841 _editor->commit_reversible_command ();
1843 // delete the dummy marker we used for visual representation of copying.
1844 // a new visual marker will show up automatically.
1847 _editor->begin_reversible_command (_("move tempo mark"));
1848 XMLNode &before = map.get_state();
1849 map.move_tempo (_marker->tempo(), when);
1850 XMLNode &after = map.get_state();
1851 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1852 _editor->commit_reversible_command ();
1857 TempoMarkerDrag::aborted ()
1859 _marker->set_position (_marker->tempo().frame());
1862 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1866 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1871 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1873 Drag::start_grab (event, c);
1877 nframes64_t where = _editor->event_frame (event, 0, 0);
1879 _editor->snap_to_with_modifier (where, event);
1880 _editor->playhead_cursor->set_position (where);
1884 if (_cursor == _editor->playhead_cursor) {
1885 _editor->_dragging_playhead = true;
1887 Session* s = _editor->session ();
1890 if (_was_rolling && _stop) {
1894 if (s->is_auditioning()) {
1895 s->cancel_audition ();
1898 s->request_suspend_timecode_transmission ();
1900 if (s->timecode_transmission_suspended ()) {
1901 nframes64_t const f = _editor->playhead_cursor->current_frame;
1902 s->send_mmc_locate (f);
1903 s->send_full_time_code (f);
1908 _pointer_frame_offset = grab_frame() - _cursor->current_frame;
1910 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1914 CursorDrag::motion (GdkEvent* event, bool)
1916 nframes64_t const adjusted_frame = adjusted_current_frame (event);
1918 if (adjusted_frame == last_pointer_frame()) {
1922 _cursor->set_position (adjusted_frame);
1924 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1926 Session* s = _editor->session ();
1927 if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
1928 nframes64_t const f = _editor->playhead_cursor->current_frame;
1929 s->send_mmc_locate (f);
1930 s->send_full_time_code (f);
1935 _editor->update_canvas_now ();
1937 _editor->UpdateAllTransportClocks (_cursor->current_frame);
1941 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
1943 _editor->_dragging_playhead = false;
1945 if (!movement_occurred && _stop) {
1949 motion (event, false);
1951 if (_item == &_editor->playhead_cursor->canvas_item) {
1952 Session* s = _editor->session ();
1954 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
1955 _editor->_pending_locate_request = true;
1956 s->request_resume_timecode_transmission ();
1962 CursorDrag::aborted ()
1964 if (_editor->_dragging_playhead) {
1965 _editor->session()->request_resume_timecode_transmission ();
1966 _editor->_dragging_playhead = false;
1969 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
1972 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1973 : RegionDrag (e, i, p, v)
1979 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1981 Drag::start_grab (event, cursor);
1983 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
1984 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
1986 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
1987 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
1989 arv->show_fade_line((nframes64_t) r->fade_in()->back()->when);
1993 FadeInDrag::motion (GdkEvent* event, bool)
1995 nframes64_t fade_length;
1997 nframes64_t const pos = adjusted_current_frame (event);
1999 boost::shared_ptr<Region> region = _primary->region ();
2001 if (pos < (region->position() + 64)) {
2002 fade_length = 64; // this should be a minimum defined somewhere
2003 } else if (pos > region->last_frame()) {
2004 fade_length = region->length();
2006 fade_length = pos - region->position();
2009 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2011 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2017 tmp->reset_fade_in_shape_width (fade_length);
2018 tmp->show_fade_line((nframes64_t) fade_length);
2021 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2025 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2027 if (!movement_occurred) {
2031 nframes64_t fade_length;
2033 nframes64_t const pos = adjusted_current_frame (event);
2035 boost::shared_ptr<Region> region = _primary->region ();
2037 if (pos < (region->position() + 64)) {
2038 fade_length = 64; // this should be a minimum defined somewhere
2039 } else if (pos > region->last_frame()) {
2040 fade_length = region->length();
2042 fade_length = pos - region->position();
2045 _editor->begin_reversible_command (_("change fade in length"));
2047 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2049 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2055 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2056 XMLNode &before = alist->get_state();
2058 tmp->audio_region()->set_fade_in_length (fade_length);
2059 tmp->audio_region()->set_fade_in_active (true);
2060 tmp->hide_fade_line();
2062 XMLNode &after = alist->get_state();
2063 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2066 _editor->commit_reversible_command ();
2070 FadeInDrag::aborted ()
2072 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2073 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2079 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2080 tmp->hide_fade_line();
2084 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2085 : RegionDrag (e, i, p, v)
2091 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2093 Drag::start_grab (event, cursor);
2095 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2096 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2098 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2099 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2101 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2105 FadeOutDrag::motion (GdkEvent* event, bool)
2107 nframes64_t fade_length;
2109 nframes64_t const pos = adjusted_current_frame (event);
2111 boost::shared_ptr<Region> region = _primary->region ();
2113 if (pos > (region->last_frame() - 64)) {
2114 fade_length = 64; // this should really be a minimum fade defined somewhere
2116 else if (pos < region->position()) {
2117 fade_length = region->length();
2120 fade_length = region->last_frame() - pos;
2123 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2125 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2131 tmp->reset_fade_out_shape_width (fade_length);
2132 tmp->show_fade_line(region->length() - fade_length);
2135 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2139 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2141 if (!movement_occurred) {
2145 nframes64_t fade_length;
2147 nframes64_t const pos = adjusted_current_frame (event);
2149 boost::shared_ptr<Region> region = _primary->region ();
2151 if (pos > (region->last_frame() - 64)) {
2152 fade_length = 64; // this should really be a minimum fade defined somewhere
2154 else if (pos < region->position()) {
2155 fade_length = region->length();
2158 fade_length = region->last_frame() - pos;
2161 _editor->begin_reversible_command (_("change fade out length"));
2163 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2165 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2171 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2172 XMLNode &before = alist->get_state();
2174 tmp->audio_region()->set_fade_out_length (fade_length);
2175 tmp->audio_region()->set_fade_out_active (true);
2176 tmp->hide_fade_line();
2178 XMLNode &after = alist->get_state();
2179 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2182 _editor->commit_reversible_command ();
2186 FadeOutDrag::aborted ()
2188 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2189 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2195 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2196 tmp->hide_fade_line();
2200 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2203 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2206 _points.push_back (Gnome::Art::Point (0, 0));
2207 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2209 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2210 _line->property_width_pixels() = 1;
2211 _line->property_points () = _points;
2214 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2217 MarkerDrag::~MarkerDrag ()
2219 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2225 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2227 Drag::start_grab (event, cursor);
2231 Location *location = _editor->find_location_from_marker (_marker, is_start);
2232 _editor->_dragging_edit_point = true;
2234 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2236 update_item (location);
2238 // _drag_line->show();
2239 // _line->raise_to_top();
2242 _editor->show_verbose_time_cursor (location->start(), 10);
2244 _editor->show_verbose_time_cursor (location->end(), 10);
2247 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2250 case Selection::Toggle:
2251 _editor->selection->toggle (_marker);
2253 case Selection::Set:
2254 if (!_editor->selection->selected (_marker)) {
2255 _editor->selection->set (_marker);
2258 case Selection::Extend:
2260 Locations::LocationList ll;
2261 list<Marker*> to_add;
2263 _editor->selection->markers.range (s, e);
2264 s = min (_marker->position(), s);
2265 e = max (_marker->position(), e);
2268 if (e < max_frames) {
2271 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2272 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2273 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2276 to_add.push_back (lm->start);
2279 to_add.push_back (lm->end);
2283 if (!to_add.empty()) {
2284 _editor->selection->add (to_add);
2288 case Selection::Add:
2289 _editor->selection->add (_marker);
2293 /* Set up copies for us to manipulate during the drag */
2295 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2296 Location* l = _editor->find_location_from_marker (*i, is_start);
2297 _copied_locations.push_back (new Location (*l));
2302 MarkerDrag::motion (GdkEvent* event, bool)
2304 nframes64_t f_delta = 0;
2306 bool move_both = false;
2308 Location *real_location;
2309 Location *copy_location = 0;
2311 nframes64_t const newframe = adjusted_current_frame (event);
2313 nframes64_t next = newframe;
2315 if (newframe == last_pointer_frame()) {
2319 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2323 MarkerSelection::iterator i;
2324 list<Location*>::iterator x;
2326 /* find the marker we're dragging, and compute the delta */
2328 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2329 x != _copied_locations.end() && i != _editor->selection->markers.end();
2335 if (marker == _marker) {
2337 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2342 if (real_location->is_mark()) {
2343 f_delta = newframe - copy_location->start();
2347 switch (marker->type()) {
2349 case Marker::LoopStart:
2350 case Marker::PunchIn:
2351 f_delta = newframe - copy_location->start();
2355 case Marker::LoopEnd:
2356 case Marker::PunchOut:
2357 f_delta = newframe - copy_location->end();
2360 /* what kind of marker is this ? */
2368 if (i == _editor->selection->markers.end()) {
2369 /* hmm, impossible - we didn't find the dragged marker */
2373 /* now move them all */
2375 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2376 x != _copied_locations.end() && i != _editor->selection->markers.end();
2382 /* call this to find out if its the start or end */
2384 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2388 if (real_location->locked()) {
2392 if (copy_location->is_mark()) {
2396 copy_location->set_start (copy_location->start() + f_delta);
2400 nframes64_t new_start = copy_location->start() + f_delta;
2401 nframes64_t new_end = copy_location->end() + f_delta;
2403 if (is_start) { // start-of-range marker
2406 copy_location->set_start (new_start);
2407 copy_location->set_end (new_end);
2408 } else if (new_start < copy_location->end()) {
2409 copy_location->set_start (new_start);
2411 _editor->snap_to (next, 1, true);
2412 copy_location->set_end (next);
2413 copy_location->set_start (newframe);
2416 } else { // end marker
2419 copy_location->set_end (new_end);
2420 copy_location->set_start (new_start);
2421 } else if (new_end > copy_location->start()) {
2422 copy_location->set_end (new_end);
2423 } else if (newframe > 0) {
2424 _editor->snap_to (next, -1, true);
2425 copy_location->set_start (next);
2426 copy_location->set_end (newframe);
2431 update_item (copy_location);
2433 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2436 lm->set_position (copy_location->start(), copy_location->end());
2440 assert (!_copied_locations.empty());
2442 _editor->show_verbose_time_cursor (newframe, 10);
2445 _editor->update_canvas_now ();
2450 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2452 if (!movement_occurred) {
2454 /* just a click, do nothing but finish
2455 off the selection process
2458 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2461 case Selection::Set:
2462 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2463 _editor->selection->set (_marker);
2467 case Selection::Toggle:
2468 case Selection::Extend:
2469 case Selection::Add:
2476 _editor->_dragging_edit_point = false;
2478 _editor->begin_reversible_command ( _("move marker") );
2479 XMLNode &before = _editor->session()->locations()->get_state();
2481 MarkerSelection::iterator i;
2482 list<Location*>::iterator x;
2485 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2486 x != _copied_locations.end() && i != _editor->selection->markers.end();
2489 Location * location = _editor->find_location_from_marker (*i, is_start);
2493 if (location->locked()) {
2497 if (location->is_mark()) {
2498 location->set_start ((*x)->start());
2500 location->set ((*x)->start(), (*x)->end());
2505 XMLNode &after = _editor->session()->locations()->get_state();
2506 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2507 _editor->commit_reversible_command ();
2513 MarkerDrag::aborted ()
2519 MarkerDrag::update_item (Location* location)
2521 double const x1 = _editor->frame_to_pixel (location->start());
2523 _points.front().set_x(x1);
2524 _points.back().set_x(x1);
2525 _line->property_points() = _points;
2528 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2530 _cumulative_x_drag (0),
2531 _cumulative_y_drag (0)
2533 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2539 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2541 Drag::start_grab (event, _editor->fader_cursor);
2543 // start the grab at the center of the control point so
2544 // the point doesn't 'jump' to the mouse after the first drag
2545 _fixed_grab_x = _point->get_x();
2546 _fixed_grab_y = _point->get_y();
2548 float const fraction = 1 - (_point->get_y() / _point->line().height());
2550 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2552 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2553 event->button.x + 10, event->button.y + 10);
2555 _editor->show_verbose_canvas_cursor ();
2559 ControlPointDrag::motion (GdkEvent* event, bool)
2561 double dx = _drags->current_pointer_x() - last_pointer_x();
2562 double dy = _drags->current_pointer_y() - last_pointer_y();
2564 if (event->button.state & Keyboard::SecondaryModifier) {
2569 /* coordinate in pixels relative to the start of the region (for region-based automation)
2570 or track (for track-based automation) */
2571 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2572 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2574 // calculate zero crossing point. back off by .01 to stay on the
2575 // positive side of zero
2576 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2578 // make sure we hit zero when passing through
2579 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2583 if (_x_constrained) {
2586 if (_y_constrained) {
2590 _cumulative_x_drag = cx - _fixed_grab_x;
2591 _cumulative_y_drag = cy - _fixed_grab_y;
2595 cy = min ((double) _point->line().height(), cy);
2597 framepos_t cx_frames = _editor->unit_to_frame (cx);
2599 if (!_x_constrained) {
2600 _editor->snap_to_with_modifier (cx_frames, event);
2603 cx_frames = min (cx_frames, _point->line().maximum_time());
2605 float const fraction = 1.0 - (cy / _point->line().height());
2607 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2609 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2611 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2615 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2617 if (!movement_occurred) {
2621 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2622 _editor->reset_point_selection ();
2626 motion (event, false);
2628 _point->line().end_drag ();
2632 ControlPointDrag::aborted ()
2634 _point->line().reset ();
2638 ControlPointDrag::active (Editing::MouseMode m)
2640 if (m == Editing::MouseGain) {
2641 /* always active in mouse gain */
2645 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2646 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2649 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2652 _cumulative_y_drag (0)
2657 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2659 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2662 _item = &_line->grab_item ();
2664 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2665 origin, and ditto for y.
2668 double cx = event->button.x;
2669 double cy = event->button.y;
2671 _line->parent_group().w2i (cx, cy);
2673 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2678 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2679 /* no adjacent points */
2683 Drag::start_grab (event, _editor->fader_cursor);
2685 /* store grab start in parent frame */
2690 double fraction = 1.0 - (cy / _line->height());
2692 _line->start_drag_line (before, after, fraction);
2694 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2695 event->button.x + 10, event->button.y + 10);
2697 _editor->show_verbose_canvas_cursor ();
2701 LineDrag::motion (GdkEvent* event, bool)
2703 double dy = _drags->current_pointer_y() - last_pointer_y();
2705 if (event->button.state & Keyboard::SecondaryModifier) {
2709 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2711 _cumulative_y_drag = cy - _fixed_grab_y;
2714 cy = min ((double) _line->height(), cy);
2716 double const fraction = 1.0 - (cy / _line->height());
2720 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2726 /* we are ignoring x position for this drag, so we can just pass in anything */
2727 _line->drag_motion (0, fraction, true, push);
2729 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2733 LineDrag::finished (GdkEvent* event, bool)
2735 motion (event, false);
2740 LineDrag::aborted ()
2745 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2748 _cumulative_x_drag (0)
2753 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2755 Drag::start_grab (event);
2757 _line = reinterpret_cast<SimpleLine*> (_item);
2760 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2762 double cx = event->button.x;
2763 double cy = event->button.y;
2765 _item->property_parent().get_value()->w2i(cx, cy);
2767 /* store grab start in parent frame */
2768 _region_view_grab_x = cx;
2770 _before = _line->property_x1();
2772 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2774 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2778 FeatureLineDrag::motion (GdkEvent* event, bool)
2780 double dx = _drags->current_pointer_x() - last_pointer_x();
2782 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2784 _cumulative_x_drag += dx;
2786 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2795 _line->property_x1() = cx;
2796 _line->property_x2() = cx;
2798 _before = _line->property_x1();
2802 FeatureLineDrag::finished (GdkEvent* event, bool)
2804 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2805 _arv->update_transient(_before, _line->property_x1());
2809 FeatureLineDrag::aborted ()
2815 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2817 Drag::start_grab (event);
2818 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2822 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2829 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2831 nframes64_t grab = grab_frame ();
2832 if (Config->get_rubberbanding_snaps_to_grid ()) {
2833 _editor->snap_to_with_modifier (grab, event);
2836 /* base start and end on initial click position */
2846 if (_drags->current_pointer_y() < grab_y()) {
2847 y1 = _drags->current_pointer_y();
2850 y2 = _drags->current_pointer_y();
2855 if (start != end || y1 != y2) {
2857 double x1 = _editor->frame_to_pixel (start);
2858 double x2 = _editor->frame_to_pixel (end);
2860 _editor->rubberband_rect->property_x1() = x1;
2861 _editor->rubberband_rect->property_y1() = y1;
2862 _editor->rubberband_rect->property_x2() = x2;
2863 _editor->rubberband_rect->property_y2() = y2;
2865 _editor->rubberband_rect->show();
2866 _editor->rubberband_rect->raise_to_top();
2868 _editor->show_verbose_time_cursor (pf, 10);
2873 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2875 if (movement_occurred) {
2877 motion (event, false);
2880 if (_drags->current_pointer_y() < grab_y()) {
2881 y1 = _drags->current_pointer_y();
2884 y2 = _drags->current_pointer_y();
2889 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2892 _editor->begin_reversible_command (_("rubberband selection"));
2894 if (grab_frame() < last_pointer_frame()) {
2895 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
2897 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
2901 _editor->commit_reversible_command ();
2905 if (!getenv("ARDOUR_SAE")) {
2906 _editor->selection->clear_tracks();
2908 _editor->selection->clear_regions();
2909 _editor->selection->clear_points ();
2910 _editor->selection->clear_lines ();
2913 _editor->rubberband_rect->hide();
2917 RubberbandSelectDrag::aborted ()
2919 _editor->rubberband_rect->hide ();
2923 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2925 Drag::start_grab (event, cursor);
2927 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2931 TimeFXDrag::motion (GdkEvent* event, bool)
2933 RegionView* rv = _primary;
2935 nframes64_t const pf = adjusted_current_frame (event);
2937 if (pf > rv->region()->position()) {
2938 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
2941 _editor->show_verbose_time_cursor (pf, 10);
2945 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2947 _primary->get_time_axis_view().hide_timestretch ();
2949 if (!movement_occurred) {
2953 if (last_pointer_frame() < _primary->region()->position()) {
2954 /* backwards drag of the left edge - not usable */
2958 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
2960 float percentage = (double) newlen / (double) _primary->region()->length();
2962 #ifndef USE_RUBBERBAND
2963 // Soundtouch uses percentage / 100 instead of normal (/ 1)
2964 if (_primary->region()->data_type() == DataType::AUDIO) {
2965 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
2969 _editor->begin_reversible_command (_("timestretch"));
2971 // XXX how do timeFX on multiple regions ?
2976 if (_editor->time_stretch (rs, percentage) == -1) {
2977 error << _("An error occurred while executing time stretch operation") << endmsg;
2982 TimeFXDrag::aborted ()
2984 _primary->get_time_axis_view().hide_timestretch ();
2989 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2991 Drag::start_grab (event);
2995 ScrubDrag::motion (GdkEvent* /*event*/, bool)
2997 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3001 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3003 if (movement_occurred && _editor->session()) {
3004 /* make sure we stop */
3005 _editor->session()->request_transport_speed (0.0);
3010 ScrubDrag::aborted ()
3015 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3019 , _original_pointer_time_axis (-1)
3020 , _last_pointer_time_axis (-1)
3026 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3028 nframes64_t start = 0;
3029 nframes64_t end = 0;
3031 if (_editor->session() == 0) {
3035 Gdk::Cursor* cursor = 0;
3037 switch (_operation) {
3038 case CreateSelection:
3039 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3044 cursor = _editor->selector_cursor;
3045 Drag::start_grab (event, cursor);
3048 case SelectionStartTrim:
3049 if (_editor->clicked_axisview) {
3050 _editor->clicked_axisview->order_selection_trims (_item, true);
3052 Drag::start_grab (event, _editor->left_side_trim_cursor);
3053 start = _editor->selection->time[_editor->clicked_selection].start;
3054 _pointer_frame_offset = grab_frame() - start;
3057 case SelectionEndTrim:
3058 if (_editor->clicked_axisview) {
3059 _editor->clicked_axisview->order_selection_trims (_item, false);
3061 Drag::start_grab (event, _editor->right_side_trim_cursor);
3062 end = _editor->selection->time[_editor->clicked_selection].end;
3063 _pointer_frame_offset = grab_frame() - end;
3067 start = _editor->selection->time[_editor->clicked_selection].start;
3068 Drag::start_grab (event, cursor);
3069 _pointer_frame_offset = grab_frame() - start;
3073 if (_operation == SelectionMove) {
3074 _editor->show_verbose_time_cursor (start, 10);
3076 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3079 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3083 SelectionDrag::motion (GdkEvent* event, bool first_move)
3085 nframes64_t start = 0;
3086 nframes64_t end = 0;
3089 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3090 if (pending_time_axis.first == 0) {
3094 nframes64_t const pending_position = adjusted_current_frame (event);
3096 /* only alter selection if things have changed */
3098 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3102 switch (_operation) {
3103 case CreateSelection:
3105 nframes64_t grab = grab_frame ();
3108 _editor->snap_to (grab);
3111 if (pending_position < grab_frame()) {
3112 start = pending_position;
3115 end = pending_position;
3119 /* first drag: Either add to the selection
3120 or create a new selection
3126 /* adding to the selection */
3127 _editor->set_selected_track_as_side_effect (Selection::Add);
3128 //_editor->selection->add (_editor->clicked_axisview);
3129 _editor->clicked_selection = _editor->selection->add (start, end);
3134 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3135 //_editor->selection->set (_editor->clicked_axisview);
3136 _editor->set_selected_track_as_side_effect (Selection::Set);
3139 _editor->clicked_selection = _editor->selection->set (start, end);
3143 /* select the track that we're in */
3144 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3145 // _editor->set_selected_track_as_side_effect (Selection::Add);
3146 _editor->selection->add (pending_time_axis.first);
3147 _added_time_axes.push_back (pending_time_axis.first);
3150 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3151 tracks that we selected in the first place.
3154 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3155 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3157 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3158 while (i != _added_time_axes.end()) {
3160 list<TimeAxisView*>::iterator tmp = i;
3163 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3164 _editor->selection->remove (*i);
3165 _added_time_axes.remove (*i);
3174 case SelectionStartTrim:
3176 start = _editor->selection->time[_editor->clicked_selection].start;
3177 end = _editor->selection->time[_editor->clicked_selection].end;
3179 if (pending_position > end) {
3182 start = pending_position;
3186 case SelectionEndTrim:
3188 start = _editor->selection->time[_editor->clicked_selection].start;
3189 end = _editor->selection->time[_editor->clicked_selection].end;
3191 if (pending_position < start) {
3194 end = pending_position;
3201 start = _editor->selection->time[_editor->clicked_selection].start;
3202 end = _editor->selection->time[_editor->clicked_selection].end;
3204 length = end - start;
3206 start = pending_position;
3207 _editor->snap_to (start);
3209 end = start + length;
3214 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3215 _editor->start_canvas_autoscroll (1, 0);
3219 _editor->selection->replace (_editor->clicked_selection, start, end);
3222 if (_operation == SelectionMove) {
3223 _editor->show_verbose_time_cursor(start, 10);
3225 _editor->show_verbose_time_cursor(pending_position, 10);
3230 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3232 Session* s = _editor->session();
3234 if (movement_occurred) {
3235 motion (event, false);
3236 /* XXX this is not object-oriented programming at all. ick */
3237 if (_editor->selection->time.consolidate()) {
3238 _editor->selection->TimeChanged ();
3241 /* XXX what if its a music time selection? */
3242 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3243 s->request_play_range (&_editor->selection->time, true);
3248 /* just a click, no pointer movement.*/
3250 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3251 _editor->selection->clear_time();
3254 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3255 _editor->selection->set (_editor->clicked_axisview);
3258 if (s && s->get_play_range () && s->transport_rolling()) {
3259 s->request_stop (false, false);
3264 _editor->stop_canvas_autoscroll ();
3268 SelectionDrag::aborted ()
3273 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3278 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3279 physical_screen_height (_editor->get_window()));
3280 _drag_rect->hide ();
3282 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3283 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3287 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3289 if (_editor->session() == 0) {
3293 Gdk::Cursor* cursor = 0;
3295 if (!_editor->temp_location) {
3296 _editor->temp_location = new Location (*_editor->session());
3299 switch (_operation) {
3300 case CreateRangeMarker:
3301 case CreateTransportMarker:
3302 case CreateCDMarker:
3304 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3309 cursor = _editor->selector_cursor;
3313 Drag::start_grab (event, cursor);
3315 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3319 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3321 nframes64_t start = 0;
3322 nframes64_t end = 0;
3323 ArdourCanvas::SimpleRect *crect;
3325 switch (_operation) {
3326 case CreateRangeMarker:
3327 crect = _editor->range_bar_drag_rect;
3329 case CreateTransportMarker:
3330 crect = _editor->transport_bar_drag_rect;
3332 case CreateCDMarker:
3333 crect = _editor->cd_marker_bar_drag_rect;
3336 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3341 nframes64_t const pf = adjusted_current_frame (event);
3343 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3344 nframes64_t grab = grab_frame ();
3345 _editor->snap_to (grab);
3347 if (pf < grab_frame()) {
3355 /* first drag: Either add to the selection
3356 or create a new selection.
3361 _editor->temp_location->set (start, end);
3365 update_item (_editor->temp_location);
3367 //_drag_rect->raise_to_top();
3372 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3373 _editor->start_canvas_autoscroll (1, 0);
3377 _editor->temp_location->set (start, end);
3379 double x1 = _editor->frame_to_pixel (start);
3380 double x2 = _editor->frame_to_pixel (end);
3381 crect->property_x1() = x1;
3382 crect->property_x2() = x2;
3384 update_item (_editor->temp_location);
3387 _editor->show_verbose_time_cursor (pf, 10);
3392 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3394 Location * newloc = 0;
3398 if (movement_occurred) {
3399 motion (event, false);
3402 switch (_operation) {
3403 case CreateRangeMarker:
3404 case CreateCDMarker:
3406 _editor->begin_reversible_command (_("new range marker"));
3407 XMLNode &before = _editor->session()->locations()->get_state();
3408 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3409 if (_operation == CreateCDMarker) {
3410 flags = Location::IsRangeMarker | Location::IsCDMarker;
3411 _editor->cd_marker_bar_drag_rect->hide();
3414 flags = Location::IsRangeMarker;
3415 _editor->range_bar_drag_rect->hide();
3417 newloc = new Location (
3418 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3421 _editor->session()->locations()->add (newloc, true);
3422 XMLNode &after = _editor->session()->locations()->get_state();
3423 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3424 _editor->commit_reversible_command ();
3428 case CreateTransportMarker:
3429 // popup menu to pick loop or punch
3430 _editor->new_transport_marker_context_menu (&event->button, _item);
3434 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3436 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3441 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3443 if (end == max_frames) {
3444 end = _editor->session()->current_end_frame ();
3447 if (start == max_frames) {
3448 start = _editor->session()->current_start_frame ();
3451 switch (_editor->mouse_mode) {
3453 /* find the two markers on either side and then make the selection from it */
3454 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3458 /* find the two markers on either side of the click and make the range out of it */
3459 _editor->selection->set (start, end);
3468 _editor->stop_canvas_autoscroll ();
3472 RangeMarkerBarDrag::aborted ()
3478 RangeMarkerBarDrag::update_item (Location* location)
3480 double const x1 = _editor->frame_to_pixel (location->start());
3481 double const x2 = _editor->frame_to_pixel (location->end());
3483 _drag_rect->property_x1() = x1;
3484 _drag_rect->property_x2() = x2;
3488 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3490 Drag::start_grab (event, _editor->zoom_cursor);
3491 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3495 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3500 nframes64_t const pf = adjusted_current_frame (event);
3502 nframes64_t grab = grab_frame ();
3503 _editor->snap_to_with_modifier (grab, event);
3505 /* base start and end on initial click position */
3517 _editor->zoom_rect->show();
3518 _editor->zoom_rect->raise_to_top();
3521 _editor->reposition_zoom_rect(start, end);
3523 _editor->show_verbose_time_cursor (pf, 10);
3528 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3530 if (movement_occurred) {
3531 motion (event, false);
3533 if (grab_frame() < last_pointer_frame()) {
3534 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3536 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3539 _editor->temporal_zoom_to_frame (false, grab_frame());
3541 temporal_zoom_step (false);
3542 center_screen (grab_frame());
3546 _editor->zoom_rect->hide();
3550 MouseZoomDrag::aborted ()
3552 _editor->zoom_rect->hide ();
3555 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3557 , _cumulative_dx (0)
3558 , _cumulative_dy (0)
3560 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3561 _region = &_primary->region_view ();
3562 _note_height = _region->midi_stream_view()->note_height ();
3566 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3568 Drag::start_grab (event);
3570 if (!(_was_selected = _primary->selected())) {
3572 /* tertiary-click means extend selection - we'll do that on button release,
3573 so don't add it here, because otherwise we make it hard to figure
3574 out the "extend-to" range.
3577 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3580 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3583 _region->note_selected (_primary, true);
3585 _region->unique_select (_primary);
3591 /** @return Current total drag x change in frames */
3593 NoteDrag::total_dx () const
3596 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3598 /* primary note time */
3599 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3601 /* new time of the primary note relative to the region position */
3602 frameoffset_t const st = n + dx;
3604 /* snap and return corresponding delta */
3605 return _region->snap_frame_to_frame (st) - n;
3608 /** @return Current total drag y change in notes */
3610 NoteDrag::total_dy () const
3612 /* this is `backwards' to make increasing note number go in the right direction */
3613 double const dy = _drags->current_pointer_y() - grab_y();
3618 if (abs (dy) >= _note_height) {
3620 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3622 ndy = (int8_t) floor (dy / _note_height / 2.0);
3631 NoteDrag::motion (GdkEvent *, bool)
3633 /* Total change in x and y since the start of the drag */
3634 frameoffset_t const dx = total_dx ();
3635 int8_t const dy = total_dy ();
3637 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3638 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3639 double const tdy = dy * _note_height - _cumulative_dy;
3642 _region->move_selection (tdx, tdy);
3643 _cumulative_dx += tdx;
3644 _cumulative_dy += tdy;
3647 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + dy).c_str(),
3648 (int) floor (_primary->note()->note() + dy));
3650 _editor->show_verbose_canvas_cursor_with (buf);
3655 NoteDrag::finished (GdkEvent* ev, bool moved)
3658 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3660 if (_was_selected) {
3661 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3663 _region->note_deselected (_primary);
3666 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3667 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3669 if (!extend && !add && _region->selection_size() > 1) {
3670 _region->unique_select (_primary);
3671 } else if (extend) {
3672 _region->note_selected (_primary, true, true);
3674 /* it was added during button press */
3679 _region->note_dropped (_primary, total_dx(), - total_dy());
3684 NoteDrag::aborted ()
3689 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3692 , _nothing_to_drag (false)
3694 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3697 _line = _atav->line ();
3701 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3703 Drag::start_grab (event, cursor);
3705 list<ControlPoint*> points;
3707 XMLNode* state = &_line->get_state ();
3709 if (_ranges.empty()) {
3711 uint32_t const N = _line->npoints ();
3712 for (uint32_t i = 0; i < N; ++i) {
3713 points.push_back (_line->nth (i));
3718 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3719 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3721 /* fade into and out of the region that we're dragging;
3722 64 samples length plucked out of thin air.
3724 nframes64_t const h = (j->start + j->end) / 2;
3725 nframes64_t a = j->start + 64;
3729 nframes64_t b = j->end - 64;
3734 the_list->add (j->start, the_list->eval (j->start));
3735 _line->add_always_in_view (j->start);
3736 the_list->add (a, the_list->eval (a));
3737 _line->add_always_in_view (a);
3738 the_list->add (b, the_list->eval (b));
3739 _line->add_always_in_view (b);
3740 the_list->add (j->end, the_list->eval (j->end));
3741 _line->add_always_in_view (j->end);
3744 uint32_t const N = _line->npoints ();
3745 for (uint32_t i = 0; i < N; ++i) {
3747 ControlPoint* p = _line->nth (i);
3749 list<AudioRange>::const_iterator j = _ranges.begin ();
3750 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3754 if (j != _ranges.end()) {
3755 points.push_back (p);
3760 if (points.empty()) {
3761 _nothing_to_drag = true;
3765 _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
3769 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3771 if (_nothing_to_drag) {
3775 float const f = 1 - (_drags->current_pointer_y() / _line->height());
3777 /* we are ignoring x position for this drag, so we can just pass in anything */
3778 _line->drag_motion (0, f, true, false);
3782 AutomationRangeDrag::finished (GdkEvent* event, bool)
3784 if (_nothing_to_drag) {
3788 motion (event, false);
3790 _line->clear_always_in_view ();
3794 AutomationRangeDrag::aborted ()
3796 _line->clear_always_in_view ();
3800 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
3803 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
3804 layer = v->region()->layer ();
3805 initial_y = v->get_canvas_group()->property_y ();
3806 initial_playlist = v->region()->playlist ();