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* a = dynamic_cast<AudioRegionView*> (_primary);
1984 boost::shared_ptr<AudioRegion> const r = a->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);
1992 FadeInDrag::motion (GdkEvent* event, bool)
1994 nframes64_t fade_length;
1996 nframes64_t const pos = adjusted_current_frame (event);
1998 boost::shared_ptr<Region> region = _primary->region ();
2000 if (pos < (region->position() + 64)) {
2001 fade_length = 64; // this should be a minimum defined somewhere
2002 } else if (pos > region->last_frame()) {
2003 fade_length = region->length();
2005 fade_length = pos - region->position();
2008 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2010 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2016 tmp->reset_fade_in_shape_width (fade_length);
2019 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2023 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2025 if (!movement_occurred) {
2029 nframes64_t fade_length;
2031 nframes64_t const pos = adjusted_current_frame (event);
2033 boost::shared_ptr<Region> region = _primary->region ();
2035 if (pos < (region->position() + 64)) {
2036 fade_length = 64; // this should be a minimum defined somewhere
2037 } else if (pos > region->last_frame()) {
2038 fade_length = region->length();
2040 fade_length = pos - region->position();
2043 _editor->begin_reversible_command (_("change fade in length"));
2045 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2047 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2053 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2054 XMLNode &before = alist->get_state();
2056 tmp->audio_region()->set_fade_in_length (fade_length);
2057 tmp->audio_region()->set_fade_in_active (true);
2059 XMLNode &after = alist->get_state();
2060 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2063 _editor->commit_reversible_command ();
2067 FadeInDrag::aborted ()
2069 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2070 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2076 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2080 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2081 : RegionDrag (e, i, p, v)
2087 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2089 Drag::start_grab (event, cursor);
2091 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2092 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2094 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2095 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2099 FadeOutDrag::motion (GdkEvent* event, bool)
2101 nframes64_t fade_length;
2103 nframes64_t const pos = adjusted_current_frame (event);
2105 boost::shared_ptr<Region> region = _primary->region ();
2107 if (pos > (region->last_frame() - 64)) {
2108 fade_length = 64; // this should really be a minimum fade defined somewhere
2110 else if (pos < region->position()) {
2111 fade_length = region->length();
2114 fade_length = region->last_frame() - pos;
2117 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2119 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2125 tmp->reset_fade_out_shape_width (fade_length);
2128 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2132 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2134 if (!movement_occurred) {
2138 nframes64_t fade_length;
2140 nframes64_t const pos = adjusted_current_frame (event);
2142 boost::shared_ptr<Region> region = _primary->region ();
2144 if (pos > (region->last_frame() - 64)) {
2145 fade_length = 64; // this should really be a minimum fade defined somewhere
2147 else if (pos < region->position()) {
2148 fade_length = region->length();
2151 fade_length = region->last_frame() - pos;
2154 _editor->begin_reversible_command (_("change fade out length"));
2156 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2158 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2164 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2165 XMLNode &before = alist->get_state();
2167 tmp->audio_region()->set_fade_out_length (fade_length);
2168 tmp->audio_region()->set_fade_out_active (true);
2170 XMLNode &after = alist->get_state();
2171 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2174 _editor->commit_reversible_command ();
2178 FadeOutDrag::aborted ()
2180 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2181 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2187 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2191 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2194 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2197 _points.push_back (Gnome::Art::Point (0, 0));
2198 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2200 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2201 _line->property_width_pixels() = 1;
2202 _line->property_points () = _points;
2205 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2208 MarkerDrag::~MarkerDrag ()
2210 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2216 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2218 Drag::start_grab (event, cursor);
2222 Location *location = _editor->find_location_from_marker (_marker, is_start);
2223 _editor->_dragging_edit_point = true;
2225 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2227 update_item (location);
2229 // _drag_line->show();
2230 // _line->raise_to_top();
2233 _editor->show_verbose_time_cursor (location->start(), 10);
2235 _editor->show_verbose_time_cursor (location->end(), 10);
2238 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2241 case Selection::Toggle:
2242 _editor->selection->toggle (_marker);
2244 case Selection::Set:
2245 if (!_editor->selection->selected (_marker)) {
2246 _editor->selection->set (_marker);
2249 case Selection::Extend:
2251 Locations::LocationList ll;
2252 list<Marker*> to_add;
2254 _editor->selection->markers.range (s, e);
2255 s = min (_marker->position(), s);
2256 e = max (_marker->position(), e);
2259 if (e < max_frames) {
2262 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2263 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2264 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2267 to_add.push_back (lm->start);
2270 to_add.push_back (lm->end);
2274 if (!to_add.empty()) {
2275 _editor->selection->add (to_add);
2279 case Selection::Add:
2280 _editor->selection->add (_marker);
2284 /* Set up copies for us to manipulate during the drag */
2286 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2287 Location* l = _editor->find_location_from_marker (*i, is_start);
2288 _copied_locations.push_back (new Location (*l));
2293 MarkerDrag::motion (GdkEvent* event, bool)
2295 nframes64_t f_delta = 0;
2297 bool move_both = false;
2299 Location *real_location;
2300 Location *copy_location = 0;
2302 nframes64_t const newframe = adjusted_current_frame (event);
2304 nframes64_t next = newframe;
2306 if (newframe == last_pointer_frame()) {
2310 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2314 MarkerSelection::iterator i;
2315 list<Location*>::iterator x;
2317 /* find the marker we're dragging, and compute the delta */
2319 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2320 x != _copied_locations.end() && i != _editor->selection->markers.end();
2326 if (marker == _marker) {
2328 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2333 if (real_location->is_mark()) {
2334 f_delta = newframe - copy_location->start();
2338 switch (marker->type()) {
2340 case Marker::LoopStart:
2341 case Marker::PunchIn:
2342 f_delta = newframe - copy_location->start();
2346 case Marker::LoopEnd:
2347 case Marker::PunchOut:
2348 f_delta = newframe - copy_location->end();
2351 /* what kind of marker is this ? */
2359 if (i == _editor->selection->markers.end()) {
2360 /* hmm, impossible - we didn't find the dragged marker */
2364 /* now move them all */
2366 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2367 x != _copied_locations.end() && i != _editor->selection->markers.end();
2373 /* call this to find out if its the start or end */
2375 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2379 if (real_location->locked()) {
2383 if (copy_location->is_mark()) {
2387 copy_location->set_start (copy_location->start() + f_delta);
2391 nframes64_t new_start = copy_location->start() + f_delta;
2392 nframes64_t new_end = copy_location->end() + f_delta;
2394 if (is_start) { // start-of-range marker
2397 copy_location->set_start (new_start);
2398 copy_location->set_end (new_end);
2399 } else if (new_start < copy_location->end()) {
2400 copy_location->set_start (new_start);
2402 _editor->snap_to (next, 1, true);
2403 copy_location->set_end (next);
2404 copy_location->set_start (newframe);
2407 } else { // end marker
2410 copy_location->set_end (new_end);
2411 copy_location->set_start (new_start);
2412 } else if (new_end > copy_location->start()) {
2413 copy_location->set_end (new_end);
2414 } else if (newframe > 0) {
2415 _editor->snap_to (next, -1, true);
2416 copy_location->set_start (next);
2417 copy_location->set_end (newframe);
2422 update_item (copy_location);
2424 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2427 lm->set_position (copy_location->start(), copy_location->end());
2431 assert (!_copied_locations.empty());
2433 _editor->show_verbose_time_cursor (newframe, 10);
2436 _editor->update_canvas_now ();
2441 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2443 if (!movement_occurred) {
2445 /* just a click, do nothing but finish
2446 off the selection process
2449 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2452 case Selection::Set:
2453 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2454 _editor->selection->set (_marker);
2458 case Selection::Toggle:
2459 case Selection::Extend:
2460 case Selection::Add:
2467 _editor->_dragging_edit_point = false;
2469 _editor->begin_reversible_command ( _("move marker") );
2470 XMLNode &before = _editor->session()->locations()->get_state();
2472 MarkerSelection::iterator i;
2473 list<Location*>::iterator x;
2476 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2477 x != _copied_locations.end() && i != _editor->selection->markers.end();
2480 Location * location = _editor->find_location_from_marker (*i, is_start);
2484 if (location->locked()) {
2488 if (location->is_mark()) {
2489 location->set_start ((*x)->start());
2491 location->set ((*x)->start(), (*x)->end());
2496 XMLNode &after = _editor->session()->locations()->get_state();
2497 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2498 _editor->commit_reversible_command ();
2504 MarkerDrag::aborted ()
2510 MarkerDrag::update_item (Location* location)
2512 double const x1 = _editor->frame_to_pixel (location->start());
2514 _points.front().set_x(x1);
2515 _points.back().set_x(x1);
2516 _line->property_points() = _points;
2519 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2521 _cumulative_x_drag (0),
2522 _cumulative_y_drag (0)
2524 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2530 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2532 Drag::start_grab (event, _editor->fader_cursor);
2534 // start the grab at the center of the control point so
2535 // the point doesn't 'jump' to the mouse after the first drag
2536 _fixed_grab_x = _point->get_x();
2537 _fixed_grab_y = _point->get_y();
2539 float const fraction = 1 - (_point->get_y() / _point->line().height());
2541 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2543 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2544 event->button.x + 10, event->button.y + 10);
2546 _editor->show_verbose_canvas_cursor ();
2550 ControlPointDrag::motion (GdkEvent* event, bool)
2552 double dx = _drags->current_pointer_x() - last_pointer_x();
2553 double dy = _drags->current_pointer_y() - last_pointer_y();
2555 if (event->button.state & Keyboard::SecondaryModifier) {
2560 /* coordinate in pixels relative to the start of the region (for region-based automation)
2561 or track (for track-based automation) */
2562 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2563 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2565 // calculate zero crossing point. back off by .01 to stay on the
2566 // positive side of zero
2567 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2569 // make sure we hit zero when passing through
2570 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2574 if (_x_constrained) {
2577 if (_y_constrained) {
2581 _cumulative_x_drag = cx - _fixed_grab_x;
2582 _cumulative_y_drag = cy - _fixed_grab_y;
2586 cy = min ((double) _point->line().height(), cy);
2588 framepos_t cx_frames = _editor->unit_to_frame (cx);
2590 if (!_x_constrained) {
2591 _editor->snap_to_with_modifier (cx_frames, event);
2594 cx_frames = min (cx_frames, _point->line().maximum_time());
2596 float const fraction = 1.0 - (cy / _point->line().height());
2598 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2600 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2602 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2606 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2608 if (!movement_occurred) {
2612 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2613 _editor->reset_point_selection ();
2617 motion (event, false);
2619 _point->line().end_drag ();
2623 ControlPointDrag::aborted ()
2625 _point->line().reset ();
2629 ControlPointDrag::active (Editing::MouseMode m)
2631 if (m == Editing::MouseGain) {
2632 /* always active in mouse gain */
2636 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2637 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2640 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2643 _cumulative_y_drag (0)
2648 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2650 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2653 _item = &_line->grab_item ();
2655 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2656 origin, and ditto for y.
2659 double cx = event->button.x;
2660 double cy = event->button.y;
2662 _line->parent_group().w2i (cx, cy);
2664 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2669 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2670 /* no adjacent points */
2674 Drag::start_grab (event, _editor->fader_cursor);
2676 /* store grab start in parent frame */
2681 double fraction = 1.0 - (cy / _line->height());
2683 _line->start_drag_line (before, after, fraction);
2685 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2686 event->button.x + 10, event->button.y + 10);
2688 _editor->show_verbose_canvas_cursor ();
2692 LineDrag::motion (GdkEvent* event, bool)
2694 double dy = _drags->current_pointer_y() - last_pointer_y();
2696 if (event->button.state & Keyboard::SecondaryModifier) {
2700 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2702 _cumulative_y_drag = cy - _fixed_grab_y;
2705 cy = min ((double) _line->height(), cy);
2707 double const fraction = 1.0 - (cy / _line->height());
2711 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2717 /* we are ignoring x position for this drag, so we can just pass in anything */
2718 _line->drag_motion (0, fraction, true, push);
2720 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2724 LineDrag::finished (GdkEvent* event, bool)
2726 motion (event, false);
2731 LineDrag::aborted ()
2736 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2739 _cumulative_x_drag (0)
2744 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2746 Drag::start_grab (event);
2748 _line = reinterpret_cast<SimpleLine*> (_item);
2751 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2753 double cx = event->button.x;
2754 double cy = event->button.y;
2756 _item->property_parent().get_value()->w2i(cx, cy);
2758 /* store grab start in parent frame */
2759 _region_view_grab_x = cx;
2761 _before = _line->property_x1();
2763 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2765 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2769 FeatureLineDrag::motion (GdkEvent* event, bool)
2771 double dx = _drags->current_pointer_x() - last_pointer_x();
2773 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2775 _cumulative_x_drag += dx;
2777 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2786 _line->property_x1() = cx;
2787 _line->property_x2() = cx;
2789 _before = _line->property_x1();
2793 FeatureLineDrag::finished (GdkEvent* event, bool)
2795 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2796 _arv->update_transient(_before, _line->property_x1());
2800 FeatureLineDrag::aborted ()
2806 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2808 Drag::start_grab (event);
2809 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2813 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2820 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2822 nframes64_t grab = grab_frame ();
2823 if (Config->get_rubberbanding_snaps_to_grid ()) {
2824 _editor->snap_to_with_modifier (grab, event);
2827 /* base start and end on initial click position */
2837 if (_drags->current_pointer_y() < grab_y()) {
2838 y1 = _drags->current_pointer_y();
2841 y2 = _drags->current_pointer_y();
2846 if (start != end || y1 != y2) {
2848 double x1 = _editor->frame_to_pixel (start);
2849 double x2 = _editor->frame_to_pixel (end);
2851 _editor->rubberband_rect->property_x1() = x1;
2852 _editor->rubberband_rect->property_y1() = y1;
2853 _editor->rubberband_rect->property_x2() = x2;
2854 _editor->rubberband_rect->property_y2() = y2;
2856 _editor->rubberband_rect->show();
2857 _editor->rubberband_rect->raise_to_top();
2859 _editor->show_verbose_time_cursor (pf, 10);
2864 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2866 if (movement_occurred) {
2868 motion (event, false);
2871 if (_drags->current_pointer_y() < grab_y()) {
2872 y1 = _drags->current_pointer_y();
2875 y2 = _drags->current_pointer_y();
2880 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2883 _editor->begin_reversible_command (_("rubberband selection"));
2885 if (grab_frame() < last_pointer_frame()) {
2886 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
2888 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
2892 _editor->commit_reversible_command ();
2896 if (!getenv("ARDOUR_SAE")) {
2897 _editor->selection->clear_tracks();
2899 _editor->selection->clear_regions();
2900 _editor->selection->clear_points ();
2901 _editor->selection->clear_lines ();
2904 _editor->rubberband_rect->hide();
2908 RubberbandSelectDrag::aborted ()
2910 _editor->rubberband_rect->hide ();
2914 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2916 Drag::start_grab (event, cursor);
2918 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2922 TimeFXDrag::motion (GdkEvent* event, bool)
2924 RegionView* rv = _primary;
2926 nframes64_t const pf = adjusted_current_frame (event);
2928 if (pf > rv->region()->position()) {
2929 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
2932 _editor->show_verbose_time_cursor (pf, 10);
2936 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2938 _primary->get_time_axis_view().hide_timestretch ();
2940 if (!movement_occurred) {
2944 if (last_pointer_frame() < _primary->region()->position()) {
2945 /* backwards drag of the left edge - not usable */
2949 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
2951 float percentage = (double) newlen / (double) _primary->region()->length();
2953 #ifndef USE_RUBBERBAND
2954 // Soundtouch uses percentage / 100 instead of normal (/ 1)
2955 if (_primary->region()->data_type() == DataType::AUDIO) {
2956 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
2960 _editor->begin_reversible_command (_("timestretch"));
2962 // XXX how do timeFX on multiple regions ?
2967 if (_editor->time_stretch (rs, percentage) == -1) {
2968 error << _("An error occurred while executing time stretch operation") << endmsg;
2973 TimeFXDrag::aborted ()
2975 _primary->get_time_axis_view().hide_timestretch ();
2980 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2982 Drag::start_grab (event);
2986 ScrubDrag::motion (GdkEvent* /*event*/, bool)
2988 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
2992 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2994 if (movement_occurred && _editor->session()) {
2995 /* make sure we stop */
2996 _editor->session()->request_transport_speed (0.0);
3001 ScrubDrag::aborted ()
3006 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3010 , _original_pointer_time_axis (-1)
3011 , _last_pointer_time_axis (-1)
3017 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3019 nframes64_t start = 0;
3020 nframes64_t end = 0;
3022 if (_editor->session() == 0) {
3026 Gdk::Cursor* cursor = 0;
3028 switch (_operation) {
3029 case CreateSelection:
3030 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3035 cursor = _editor->selector_cursor;
3036 Drag::start_grab (event, cursor);
3039 case SelectionStartTrim:
3040 if (_editor->clicked_axisview) {
3041 _editor->clicked_axisview->order_selection_trims (_item, true);
3043 Drag::start_grab (event, _editor->left_side_trim_cursor);
3044 start = _editor->selection->time[_editor->clicked_selection].start;
3045 _pointer_frame_offset = grab_frame() - start;
3048 case SelectionEndTrim:
3049 if (_editor->clicked_axisview) {
3050 _editor->clicked_axisview->order_selection_trims (_item, false);
3052 Drag::start_grab (event, _editor->right_side_trim_cursor);
3053 end = _editor->selection->time[_editor->clicked_selection].end;
3054 _pointer_frame_offset = grab_frame() - end;
3058 start = _editor->selection->time[_editor->clicked_selection].start;
3059 Drag::start_grab (event, cursor);
3060 _pointer_frame_offset = grab_frame() - start;
3064 if (_operation == SelectionMove) {
3065 _editor->show_verbose_time_cursor (start, 10);
3067 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3070 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3074 SelectionDrag::motion (GdkEvent* event, bool first_move)
3076 nframes64_t start = 0;
3077 nframes64_t end = 0;
3080 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3081 if (pending_time_axis.first == 0) {
3085 nframes64_t const pending_position = adjusted_current_frame (event);
3087 /* only alter selection if things have changed */
3089 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3093 switch (_operation) {
3094 case CreateSelection:
3096 nframes64_t grab = grab_frame ();
3099 _editor->snap_to (grab);
3102 if (pending_position < grab_frame()) {
3103 start = pending_position;
3106 end = pending_position;
3110 /* first drag: Either add to the selection
3111 or create a new selection
3117 /* adding to the selection */
3118 _editor->set_selected_track_as_side_effect (Selection::Add);
3119 //_editor->selection->add (_editor->clicked_axisview);
3120 _editor->clicked_selection = _editor->selection->add (start, end);
3125 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3126 //_editor->selection->set (_editor->clicked_axisview);
3127 _editor->set_selected_track_as_side_effect (Selection::Set);
3130 _editor->clicked_selection = _editor->selection->set (start, end);
3134 /* select the track that we're in */
3135 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3136 // _editor->set_selected_track_as_side_effect (Selection::Add);
3137 _editor->selection->add (pending_time_axis.first);
3138 _added_time_axes.push_back (pending_time_axis.first);
3141 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3142 tracks that we selected in the first place.
3145 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3146 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3148 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3149 while (i != _added_time_axes.end()) {
3151 list<TimeAxisView*>::iterator tmp = i;
3154 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3155 _editor->selection->remove (*i);
3156 _added_time_axes.remove (*i);
3165 case SelectionStartTrim:
3167 start = _editor->selection->time[_editor->clicked_selection].start;
3168 end = _editor->selection->time[_editor->clicked_selection].end;
3170 if (pending_position > end) {
3173 start = pending_position;
3177 case SelectionEndTrim:
3179 start = _editor->selection->time[_editor->clicked_selection].start;
3180 end = _editor->selection->time[_editor->clicked_selection].end;
3182 if (pending_position < start) {
3185 end = pending_position;
3192 start = _editor->selection->time[_editor->clicked_selection].start;
3193 end = _editor->selection->time[_editor->clicked_selection].end;
3195 length = end - start;
3197 start = pending_position;
3198 _editor->snap_to (start);
3200 end = start + length;
3205 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3206 _editor->start_canvas_autoscroll (1, 0);
3210 _editor->selection->replace (_editor->clicked_selection, start, end);
3213 if (_operation == SelectionMove) {
3214 _editor->show_verbose_time_cursor(start, 10);
3216 _editor->show_verbose_time_cursor(pending_position, 10);
3221 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3223 Session* s = _editor->session();
3225 if (movement_occurred) {
3226 motion (event, false);
3227 /* XXX this is not object-oriented programming at all. ick */
3228 if (_editor->selection->time.consolidate()) {
3229 _editor->selection->TimeChanged ();
3232 /* XXX what if its a music time selection? */
3233 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3234 s->request_play_range (&_editor->selection->time, true);
3239 /* just a click, no pointer movement.*/
3241 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3242 _editor->selection->clear_time();
3245 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3246 _editor->selection->set (_editor->clicked_axisview);
3249 if (s && s->get_play_range () && s->transport_rolling()) {
3250 s->request_stop (false, false);
3255 _editor->stop_canvas_autoscroll ();
3259 SelectionDrag::aborted ()
3264 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3269 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3270 physical_screen_height (_editor->get_window()));
3271 _drag_rect->hide ();
3273 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3274 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3278 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3280 if (_editor->session() == 0) {
3284 Gdk::Cursor* cursor = 0;
3286 if (!_editor->temp_location) {
3287 _editor->temp_location = new Location (*_editor->session());
3290 switch (_operation) {
3291 case CreateRangeMarker:
3292 case CreateTransportMarker:
3293 case CreateCDMarker:
3295 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3300 cursor = _editor->selector_cursor;
3304 Drag::start_grab (event, cursor);
3306 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3310 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3312 nframes64_t start = 0;
3313 nframes64_t end = 0;
3314 ArdourCanvas::SimpleRect *crect;
3316 switch (_operation) {
3317 case CreateRangeMarker:
3318 crect = _editor->range_bar_drag_rect;
3320 case CreateTransportMarker:
3321 crect = _editor->transport_bar_drag_rect;
3323 case CreateCDMarker:
3324 crect = _editor->cd_marker_bar_drag_rect;
3327 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3332 nframes64_t const pf = adjusted_current_frame (event);
3334 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3335 nframes64_t grab = grab_frame ();
3336 _editor->snap_to (grab);
3338 if (pf < grab_frame()) {
3346 /* first drag: Either add to the selection
3347 or create a new selection.
3352 _editor->temp_location->set (start, end);
3356 update_item (_editor->temp_location);
3358 //_drag_rect->raise_to_top();
3363 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3364 _editor->start_canvas_autoscroll (1, 0);
3368 _editor->temp_location->set (start, end);
3370 double x1 = _editor->frame_to_pixel (start);
3371 double x2 = _editor->frame_to_pixel (end);
3372 crect->property_x1() = x1;
3373 crect->property_x2() = x2;
3375 update_item (_editor->temp_location);
3378 _editor->show_verbose_time_cursor (pf, 10);
3383 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3385 Location * newloc = 0;
3389 if (movement_occurred) {
3390 motion (event, false);
3393 switch (_operation) {
3394 case CreateRangeMarker:
3395 case CreateCDMarker:
3397 _editor->begin_reversible_command (_("new range marker"));
3398 XMLNode &before = _editor->session()->locations()->get_state();
3399 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3400 if (_operation == CreateCDMarker) {
3401 flags = Location::IsRangeMarker | Location::IsCDMarker;
3402 _editor->cd_marker_bar_drag_rect->hide();
3405 flags = Location::IsRangeMarker;
3406 _editor->range_bar_drag_rect->hide();
3408 newloc = new Location (
3409 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3412 _editor->session()->locations()->add (newloc, true);
3413 XMLNode &after = _editor->session()->locations()->get_state();
3414 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3415 _editor->commit_reversible_command ();
3419 case CreateTransportMarker:
3420 // popup menu to pick loop or punch
3421 _editor->new_transport_marker_context_menu (&event->button, _item);
3425 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3427 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3432 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3434 if (end == max_frames) {
3435 end = _editor->session()->current_end_frame ();
3438 if (start == max_frames) {
3439 start = _editor->session()->current_start_frame ();
3442 switch (_editor->mouse_mode) {
3444 /* find the two markers on either side and then make the selection from it */
3445 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3449 /* find the two markers on either side of the click and make the range out of it */
3450 _editor->selection->set (start, end);
3459 _editor->stop_canvas_autoscroll ();
3463 RangeMarkerBarDrag::aborted ()
3469 RangeMarkerBarDrag::update_item (Location* location)
3471 double const x1 = _editor->frame_to_pixel (location->start());
3472 double const x2 = _editor->frame_to_pixel (location->end());
3474 _drag_rect->property_x1() = x1;
3475 _drag_rect->property_x2() = x2;
3479 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3481 Drag::start_grab (event, _editor->zoom_cursor);
3482 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3486 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3491 nframes64_t const pf = adjusted_current_frame (event);
3493 nframes64_t grab = grab_frame ();
3494 _editor->snap_to_with_modifier (grab, event);
3496 /* base start and end on initial click position */
3508 _editor->zoom_rect->show();
3509 _editor->zoom_rect->raise_to_top();
3512 _editor->reposition_zoom_rect(start, end);
3514 _editor->show_verbose_time_cursor (pf, 10);
3519 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3521 if (movement_occurred) {
3522 motion (event, false);
3524 if (grab_frame() < last_pointer_frame()) {
3525 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3527 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3530 _editor->temporal_zoom_to_frame (false, grab_frame());
3532 temporal_zoom_step (false);
3533 center_screen (grab_frame());
3537 _editor->zoom_rect->hide();
3541 MouseZoomDrag::aborted ()
3543 _editor->zoom_rect->hide ();
3546 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3548 , _cumulative_dx (0)
3549 , _cumulative_dy (0)
3551 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3552 _region = &_primary->region_view ();
3553 _note_height = _region->midi_stream_view()->note_height ();
3557 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3559 Drag::start_grab (event);
3561 if (!(_was_selected = _primary->selected())) {
3563 /* tertiary-click means extend selection - we'll do that on button release,
3564 so don't add it here, because otherwise we make it hard to figure
3565 out the "extend-to" range.
3568 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3571 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3574 _region->note_selected (_primary, true);
3576 _region->unique_select (_primary);
3582 /** @return Current total drag x change in frames */
3584 NoteDrag::total_dx () const
3587 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3589 /* primary note time */
3590 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3592 /* new time of the primary note relative to the region position */
3593 frameoffset_t const st = n + dx;
3595 /* snap and return corresponding delta */
3596 return _region->snap_frame_to_frame (st) - n;
3599 /** @return Current total drag y change in notes */
3601 NoteDrag::total_dy () const
3603 /* this is `backwards' to make increasing note number go in the right direction */
3604 double const dy = _drags->current_pointer_y() - grab_y();
3609 if (abs (dy) >= _note_height) {
3611 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3613 ndy = (int8_t) floor (dy / _note_height / 2.0);
3622 NoteDrag::motion (GdkEvent *, bool)
3624 /* Total change in x and y since the start of the drag */
3625 frameoffset_t const dx = total_dx ();
3626 int8_t const dy = total_dy ();
3628 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3629 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3630 double const tdy = dy * _note_height - _cumulative_dy;
3633 _region->move_selection (tdx, tdy);
3634 _cumulative_dx += tdx;
3635 _cumulative_dy += tdy;
3638 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + dy).c_str(),
3639 (int) floor (_primary->note()->note() + dy));
3641 _editor->show_verbose_canvas_cursor_with (buf);
3646 NoteDrag::finished (GdkEvent* ev, bool moved)
3649 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3651 if (_was_selected) {
3652 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3654 _region->note_deselected (_primary);
3657 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3658 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3660 if (!extend && !add && _region->selection_size() > 1) {
3661 _region->unique_select (_primary);
3662 } else if (extend) {
3663 _region->note_selected (_primary, true, true);
3665 /* it was added during button press */
3670 _region->note_dropped (_primary, total_dx(), - total_dy());
3675 NoteDrag::aborted ()
3680 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3683 , _nothing_to_drag (false)
3685 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3688 _line = _atav->line ();
3692 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3694 Drag::start_grab (event, cursor);
3696 list<ControlPoint*> points;
3698 XMLNode* state = &_line->get_state ();
3700 if (_ranges.empty()) {
3702 uint32_t const N = _line->npoints ();
3703 for (uint32_t i = 0; i < N; ++i) {
3704 points.push_back (_line->nth (i));
3709 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3710 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3712 /* fade into and out of the region that we're dragging;
3713 64 samples length plucked out of thin air.
3715 nframes64_t const h = (j->start + j->end) / 2;
3716 nframes64_t a = j->start + 64;
3720 nframes64_t b = j->end - 64;
3725 the_list->add (j->start, the_list->eval (j->start));
3726 _line->add_always_in_view (j->start);
3727 the_list->add (a, the_list->eval (a));
3728 _line->add_always_in_view (a);
3729 the_list->add (b, the_list->eval (b));
3730 _line->add_always_in_view (b);
3731 the_list->add (j->end, the_list->eval (j->end));
3732 _line->add_always_in_view (j->end);
3735 uint32_t const N = _line->npoints ();
3736 for (uint32_t i = 0; i < N; ++i) {
3738 ControlPoint* p = _line->nth (i);
3740 list<AudioRange>::const_iterator j = _ranges.begin ();
3741 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3745 if (j != _ranges.end()) {
3746 points.push_back (p);
3751 if (points.empty()) {
3752 _nothing_to_drag = true;
3756 _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
3760 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3762 if (_nothing_to_drag) {
3766 float const f = 1 - (_drags->current_pointer_y() / _line->height());
3768 /* we are ignoring x position for this drag, so we can just pass in anything */
3769 _line->drag_motion (0, f, true, false);
3773 AutomationRangeDrag::finished (GdkEvent* event, bool)
3775 if (_nothing_to_drag) {
3779 motion (event, false);
3781 _line->clear_always_in_view ();
3785 AutomationRangeDrag::aborted ()
3787 _line->clear_always_in_view ();
3791 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
3794 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
3795 layer = v->region()->layer ();
3796 initial_y = v->get_canvas_group()->property_y ();
3797 initial_playlist = v->region()->playlist ();