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)
170 , _raw_grab_frame (0)
172 , _last_pointer_frame (0)
178 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
184 cursor = _editor->which_grabber_cursor ();
187 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
191 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
194 cursor = _editor->which_grabber_cursor ();
197 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
199 if (Keyboard::is_button2_event (&event->button)) {
200 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
201 _y_constrained = true;
202 _x_constrained = false;
204 _y_constrained = false;
205 _x_constrained = true;
208 _x_constrained = false;
209 _y_constrained = false;
212 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
213 _grab_frame = adjusted_frame (_raw_grab_frame, event);
214 _last_pointer_frame = _grab_frame;
215 _last_pointer_x = _grab_x;
216 _last_pointer_y = _grab_y;
218 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
222 if (_editor->session() && _editor->session()->transport_rolling()) {
225 _was_rolling = false;
228 switch (_editor->snap_type()) {
229 case SnapToRegionStart:
230 case SnapToRegionEnd:
231 case SnapToRegionSync:
232 case SnapToRegionBoundary:
233 _editor->build_region_boundary_cache ();
240 /** Call to end a drag `successfully'. Ungrabs item and calls
241 * subclass' finished() method.
243 * @param event GDK event, or 0.
244 * @return true if some movement occurred, otherwise false.
247 Drag::end_grab (GdkEvent* event)
249 _editor->stop_canvas_autoscroll ();
251 _item->ungrab (event ? event->button.time : 0);
253 finished (event, _move_threshold_passed);
255 _editor->hide_verbose_canvas_cursor();
257 return _move_threshold_passed;
261 Drag::adjusted_frame (nframes64_t f, GdkEvent const * event, bool snap) const
265 if (f > _pointer_frame_offset) {
266 pos = f - _pointer_frame_offset;
270 _editor->snap_to_with_modifier (pos, event);
277 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
279 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
283 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
285 /* check to see if we have moved in any way that matters since the last motion event */
286 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
287 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
291 pair<nframes64_t, int> const threshold = move_threshold ();
293 bool const old_move_threshold_passed = _move_threshold_passed;
295 if (!from_autoscroll && !_move_threshold_passed) {
297 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
298 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
300 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
303 if (active (_editor->mouse_mode) && _move_threshold_passed) {
305 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
306 if (!from_autoscroll) {
307 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
310 motion (event, _move_threshold_passed != old_move_threshold_passed);
312 _last_pointer_x = _drags->current_pointer_x ();
313 _last_pointer_y = _drags->current_pointer_y ();
314 _last_pointer_frame = adjusted_current_frame (event);
322 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
332 _editor->stop_canvas_autoscroll ();
333 _editor->hide_verbose_canvas_cursor ();
336 struct EditorOrderTimeAxisViewSorter {
337 bool operator() (TimeAxisView* a, TimeAxisView* b) {
338 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
339 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
341 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
345 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
349 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
351 /* Make a list of non-hidden tracks to refer to during the drag */
353 TrackViewList track_views = _editor->track_views;
354 track_views.sort (EditorOrderTimeAxisViewSorter ());
356 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
357 if (!(*i)->hidden()) {
359 _time_axis_views.push_back (*i);
361 TimeAxisView::Children children_list = (*i)->get_child_list ();
362 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
363 _time_axis_views.push_back (j->get());
368 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
369 _views.push_back (DraggingView (*i, this));
372 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
376 RegionDrag::region_going_away (RegionView* v)
378 list<DraggingView>::iterator i = _views.begin ();
379 while (i != _views.end() && i->view != v) {
383 if (i != _views.end()) {
388 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
390 RegionDrag::find_time_axis_view (TimeAxisView* t) const
393 int const N = _time_axis_views.size ();
394 while (i < N && _time_axis_views[i] != t) {
405 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
406 : RegionDrag (e, i, p, v),
415 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
417 Drag::start_grab (event, cursor);
419 _editor->show_verbose_time_cursor (_last_frame_position, 10);
421 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
422 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
423 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
427 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
429 /* compute the amount of pointer motion in frames, and where
430 the region would be if we moved it by that much.
432 *pending_region_position = adjusted_current_frame (event);
434 nframes64_t sync_frame;
435 nframes64_t sync_offset;
438 sync_offset = _primary->region()->sync_offset (sync_dir);
440 /* we don't handle a sync point that lies before zero.
442 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
444 sync_frame = *pending_region_position + (sync_dir*sync_offset);
446 _editor->snap_to_with_modifier (sync_frame, event);
448 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
451 *pending_region_position = _last_frame_position;
454 if (*pending_region_position > max_frames - _primary->region()->length()) {
455 *pending_region_position = _last_frame_position;
460 /* in locked edit mode, reverse the usual meaning of _x_constrained */
461 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
463 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
465 /* x movement since last time */
466 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
468 /* total x movement */
469 framecnt_t total_dx = *pending_region_position;
470 if (regions_came_from_canvas()) {
471 total_dx = total_dx - grab_frame () + _pointer_frame_offset;
474 /* check that no regions have gone off the start of the session */
475 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
476 if ((i->view->region()->position() + total_dx) < 0) {
478 *pending_region_position = _last_frame_position;
483 _last_frame_position = *pending_region_position;
490 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
492 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
493 int const n = i->time_axis_view + delta_track;
494 if (n < 0 || n >= int (_time_axis_views.size())) {
495 /* off the top or bottom track */
499 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
500 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
501 /* not a track, or the wrong type */
505 int const l = i->layer + delta_layer;
506 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
507 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
508 If it has, the layers will be munged later anyway, so it's ok.
514 /* all regions being dragged are ok with this change */
519 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
521 /* Find the TimeAxisView that the pointer is now over */
522 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
524 /* Bail early if we're not over a track */
525 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
526 if (!rtv || !rtv->is_track()) {
527 _editor->hide_verbose_canvas_cursor ();
531 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
533 /* Here's the current pointer position in terms of time axis view and layer */
534 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
535 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
537 /* Work out the change in x */
538 framepos_t pending_region_position;
539 double const x_delta = compute_x_delta (event, &pending_region_position);
541 /* Work out the change in y */
542 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
543 int delta_layer = current_pointer_layer - _last_pointer_layer;
545 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
546 /* this y movement is not allowed, so do no y movement this time */
547 delta_time_axis_view = 0;
551 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
552 /* haven't reached next snap point, and we're not switching
553 trackviews nor layers. nothing to do.
558 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
560 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
562 RegionView* rv = i->view;
564 if (rv->region()->locked()) {
570 /* here we are calculating the y distance from the
571 top of the first track view to the top of the region
572 area of the track view that we're working on */
574 /* this x value is just a dummy value so that we have something
579 /* distance from the top of this track view to the region area
580 of our track view is always 1 */
584 /* convert to world coordinates, ie distance from the top of
587 rv->get_canvas_frame()->i2w (ix1, iy1);
589 /* compensate for the ruler section and the vertical scrollbar position */
590 iy1 += _editor->get_trackview_group_vertical_offset ();
592 // hide any dependent views
594 rv->get_time_axis_view().hide_dependent_views (*rv);
597 reparent to a non scrolling group so that we can keep the
598 region selection above all time axis views.
599 reparenting means we have to move the rv as the two
600 parent groups have different coordinates.
603 rv->get_canvas_group()->property_y() = iy1 - 1;
604 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
606 rv->fake_set_opaque (true);
609 /* Work out the change in y position of this region view */
613 /* If we have moved tracks, we'll fudge the layer delta so that the
614 region gets moved back onto layer 0 on its new track; this avoids
615 confusion when dragging regions from non-zero layers onto different
618 int this_delta_layer = delta_layer;
619 if (delta_time_axis_view != 0) {
620 this_delta_layer = - i->layer;
623 /* Move this region to layer 0 on its old track */
624 StreamView* lv = _time_axis_views[i->time_axis_view]->view ();
625 if (lv->layer_display() == Stacked) {
626 y_delta -= (lv->layers() - i->layer - 1) * lv->child_height ();
629 /* Now move it to its right layer on the current track */
630 StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view ();
631 if (cv->layer_display() == Stacked) {
632 y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height ();
636 if (delta_time_axis_view > 0) {
637 for (int j = 0; j < delta_time_axis_view; ++j) {
638 y_delta += _time_axis_views[i->time_axis_view + j]->current_height ();
641 /* start by subtracting the height of the track above where we are now */
642 for (int j = 1; j <= -delta_time_axis_view; ++j) {
643 y_delta -= _time_axis_views[i->time_axis_view - j]->current_height ();
648 rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ());
650 /* Update the DraggingView */
651 i->time_axis_view += delta_time_axis_view;
652 i->layer += this_delta_layer;
655 _editor->mouse_brush_insert_region (rv, pending_region_position);
657 rv->move (x_delta, y_delta);
660 } /* foreach region */
662 _total_x_delta += x_delta;
665 _editor->cursor_group->raise_to_top();
668 if (x_delta != 0 && !_brushing) {
669 _editor->show_verbose_time_cursor (_last_frame_position, 10);
672 _last_pointer_time_axis_view += delta_time_axis_view;
673 _last_pointer_layer += delta_layer;
677 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
679 if (_copy && first_move) {
681 /* duplicate the regionview(s) and region(s) */
683 list<DraggingView> new_regionviews;
685 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
687 RegionView* rv = i->view;
688 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
689 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
691 const boost::shared_ptr<const Region> original = rv->region();
692 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
693 region_copy->set_position (original->position(), this);
697 boost::shared_ptr<AudioRegion> audioregion_copy
698 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
700 nrv = new AudioRegionView (*arv, audioregion_copy);
702 boost::shared_ptr<MidiRegion> midiregion_copy
703 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
704 nrv = new MidiRegionView (*mrv, midiregion_copy);
709 nrv->get_canvas_group()->show ();
710 new_regionviews.push_back (DraggingView (nrv, this));
712 /* swap _primary to the copy */
714 if (rv == _primary) {
718 /* ..and deselect the one we copied */
720 rv->set_selected (false);
723 if (!new_regionviews.empty()) {
725 /* reflect the fact that we are dragging the copies */
727 _views = new_regionviews;
729 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
732 sync the canvas to what we think is its current state
733 without it, the canvas seems to
734 "forget" to update properly after the upcoming reparent()
735 ..only if the mouse is in rapid motion at the time of the grab.
736 something to do with regionview creation taking so long?
738 _editor->update_canvas_now();
742 RegionMotionDrag::motion (event, first_move);
746 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
748 if (!movement_occurred) {
753 /* reverse this here so that we have the correct logic to finalize
757 if (Config->get_edit_mode() == Lock) {
758 _x_constrained = !_x_constrained;
761 bool const changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
762 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
763 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
765 _editor->update_canvas_now ();
787 RegionMoveDrag::finished_copy (
788 bool const changed_position,
789 bool const changed_tracks,
790 framecnt_t const drag_delta
793 RegionSelection new_views;
794 PlaylistSet modified_playlists;
795 list<RegionView*> views_to_delete;
798 /* all changes were made during motion event handlers */
800 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
804 _editor->commit_reversible_command ();
808 if (_x_constrained) {
809 _editor->begin_reversible_command (_("fixed time region copy"));
811 _editor->begin_reversible_command (_("region copy"));
814 /* insert the regions into their new playlists */
815 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
817 if (i->view->region()->locked()) {
823 if (changed_position && !_x_constrained) {
824 where = i->view->region()->position() - drag_delta;
826 where = i->view->region()->position();
829 RegionView* new_view = insert_region_into_playlist (
830 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
837 new_views.push_back (new_view);
839 /* we don't need the copied RegionView any more */
840 views_to_delete.push_back (i->view);
843 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
844 because when views are deleted they are automagically removed from _views, which messes
847 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
851 /* If we've created new regions either by copying or moving
852 to a new track, we want to replace the old selection with the new ones
855 if (new_views.size() > 0) {
856 _editor->selection->set (new_views);
859 /* write commands for the accumulated diffs for all our modified playlists */
860 add_stateful_diff_commands_for_playlists (modified_playlists);
862 _editor->commit_reversible_command ();
866 RegionMoveDrag::finished_no_copy (
867 bool const changed_position,
868 bool const changed_tracks,
869 framecnt_t const drag_delta
872 RegionSelection new_views;
873 PlaylistSet modified_playlists;
874 PlaylistSet frozen_playlists;
877 /* all changes were made during motion event handlers */
878 _editor->commit_reversible_command ();
882 if (_x_constrained) {
883 _editor->begin_reversible_command (_("fixed time region drag"));
885 _editor->begin_reversible_command (_("region drag"));
888 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
890 RegionView* rv = i->view;
892 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
893 layer_t const dest_layer = i->layer;
895 if (rv->region()->locked()) {
902 if (changed_position && !_x_constrained) {
903 where = rv->region()->position() - drag_delta;
905 where = rv->region()->position();
908 if (changed_tracks) {
910 /* insert into new playlist */
912 RegionView* new_view = insert_region_into_playlist (
913 RegionFactory::create (rv->region ()), dest_rtv, dest_layer, where, modified_playlists
921 new_views.push_back (new_view);
923 /* remove from old playlist */
925 /* the region that used to be in the old playlist is not
926 moved to the new one - we use a copy of it. as a result,
927 any existing editor for the region should no longer be
930 rv->hide_region_editor();
931 rv->fake_set_opaque (false);
933 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
937 rv->region()->clear_changes ();
940 motion on the same track. plonk the previously reparented region
941 back to its original canvas group (its streamview).
942 No need to do anything for copies as they are fake regions which will be deleted.
945 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
946 rv->get_canvas_group()->property_y() = i->initial_y;
947 rv->get_time_axis_view().reveal_dependent_views (*rv);
949 /* just change the model */
951 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
953 if (dest_rtv->view()->layer_display() == Stacked) {
954 rv->region()->set_layer (dest_layer);
955 rv->region()->set_pending_explicit_relayer (true);
958 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
960 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
966 /* this movement may result in a crossfade being modified, so we need to get undo
967 data from the playlist as well as the region.
970 r = modified_playlists.insert (playlist);
972 playlist->clear_changes ();
975 rv->region()->set_position (where, (void*) this);
977 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
980 if (changed_tracks) {
982 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
983 was selected in all of them, then removing it from a playlist will have removed all
984 trace of it from _views (i.e. there were N regions selected, we removed 1,
985 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
986 corresponding regionview, and _views is now empty).
988 This could have invalidated any and all iterators into _views.
990 The heuristic we use here is: if the region selection is empty, break out of the loop
991 here. if the region selection is not empty, then restart the loop because we know that
992 we must have removed at least the region(view) we've just been working on as well as any
993 that we processed on previous iterations.
995 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1000 if (_views.empty()) {
1011 /* If we've created new regions either by copying or moving
1012 to a new track, we want to replace the old selection with the new ones
1015 if (new_views.size() > 0) {
1016 _editor->selection->set (new_views);
1019 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1023 /* write commands for the accumulated diffs for all our modified playlists */
1024 add_stateful_diff_commands_for_playlists (modified_playlists);
1026 _editor->commit_reversible_command ();
1029 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1030 * @param region Region to remove.
1031 * @param playlist playlist To remove from.
1032 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1033 * that clear_changes () is only called once per playlist.
1036 RegionMoveDrag::remove_region_from_playlist (
1037 boost::shared_ptr<Region> region,
1038 boost::shared_ptr<Playlist> playlist,
1039 PlaylistSet& modified_playlists
1042 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1045 playlist->clear_changes ();
1048 playlist->remove_region (region);
1052 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1053 * clearing the playlist's diff history first if necessary.
1054 * @param region Region to insert.
1055 * @param dest_rtv Destination RouteTimeAxisView.
1056 * @param dest_layer Destination layer.
1057 * @param where Destination position.
1058 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1059 * that clear_changes () is only called once per playlist.
1060 * @return New RegionView, or 0 if no insert was performed.
1063 RegionMoveDrag::insert_region_into_playlist (
1064 boost::shared_ptr<Region> region,
1065 RouteTimeAxisView* dest_rtv,
1068 PlaylistSet& modified_playlists
1071 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1072 if (!dest_playlist) {
1076 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1077 _new_region_view = 0;
1078 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1080 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1081 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1083 dest_playlist->clear_changes ();
1086 dest_playlist->add_region (region, where);
1088 if (dest_rtv->view()->layer_display() == Stacked) {
1089 region->set_layer (dest_layer);
1090 region->set_pending_explicit_relayer (true);
1095 assert (_new_region_view);
1097 return _new_region_view;
1101 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1103 _new_region_view = rv;
1107 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1109 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1110 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1112 _editor->session()->add_command (new StatefulDiffCommand (*i));
1121 RegionMoveDrag::aborted ()
1125 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1132 RegionMotionDrag::aborted ();
1137 RegionMotionDrag::aborted ()
1139 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1140 RegionView* rv = i->view;
1141 TimeAxisView* tv = &(rv->get_time_axis_view ());
1142 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1144 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1145 rv->get_canvas_group()->property_y() = 0;
1146 rv->get_time_axis_view().reveal_dependent_views (*rv);
1147 rv->fake_set_opaque (false);
1148 rv->move (-_total_x_delta, 0);
1149 rv->set_height (rtv->view()->child_height ());
1152 _editor->update_canvas_now ();
1155 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1156 : RegionMotionDrag (e, i, p, v, b),
1160 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1161 if (rtv && rtv->is_track()) {
1162 speed = rtv->track()->speed ();
1165 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1169 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1171 RegionMotionDrag::start_grab (event, c);
1173 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1176 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1177 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1179 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1180 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1182 _primary = v->view()->create_region_view (r, false, false);
1184 _primary->get_canvas_group()->show ();
1185 _primary->set_position (pos, 0);
1186 _views.push_back (DraggingView (_primary, this));
1188 _last_frame_position = pos;
1190 _item = _primary->get_canvas_group ();
1194 RegionInsertDrag::finished (GdkEvent *, bool)
1196 _editor->update_canvas_now ();
1198 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1200 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1201 _primary->get_canvas_group()->property_y() = 0;
1203 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1205 _editor->begin_reversible_command (_("insert region"));
1206 playlist->clear_changes ();
1207 playlist->add_region (_primary->region (), _last_frame_position);
1208 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1209 _editor->commit_reversible_command ();
1217 RegionInsertDrag::aborted ()
1224 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1225 : RegionMoveDrag (e, i, p, v, false, false)
1230 struct RegionSelectionByPosition {
1231 bool operator() (RegionView*a, RegionView* b) {
1232 return a->region()->position () < b->region()->position();
1237 RegionSpliceDrag::motion (GdkEvent* event, bool)
1239 /* Which trackview is this ? */
1241 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1242 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1243 layer_t layer = tvp.second;
1245 if (tv && tv->layer_display() == Overlaid) {
1249 /* The region motion is only processed if the pointer is over
1253 if (!tv || !tv->is_track()) {
1254 /* To make sure we hide the verbose canvas cursor when the mouse is
1255 not held over and audiotrack.
1257 _editor->hide_verbose_canvas_cursor ();
1263 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1269 RegionSelection copy (_editor->selection->regions);
1271 RegionSelectionByPosition cmp;
1274 nframes64_t const pf = adjusted_current_frame (event);
1276 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1278 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1284 boost::shared_ptr<Playlist> playlist;
1286 if ((playlist = atv->playlist()) == 0) {
1290 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1295 if (pf < (*i)->region()->last_frame() + 1) {
1299 if (pf > (*i)->region()->first_frame()) {
1305 playlist->shuffle ((*i)->region(), dir);
1310 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1312 RegionMoveDrag::finished (event, movement_occurred);
1316 RegionSpliceDrag::aborted ()
1321 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1323 _view (dynamic_cast<MidiTimeAxisView*> (v))
1329 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1332 /* don't use a zero-length region otherwise its region view will be hidden when it is created */
1333 _region = _view->add_region (grab_frame(), 1, false);
1335 framepos_t const f = adjusted_current_frame (event);
1336 if (f < grab_frame()) {
1337 _region->set_position (f, this);
1340 /* again, don't use a zero-length region (see above) */
1341 framecnt_t const len = abs (f - grab_frame ());
1342 _region->set_length (len < 1 ? 1 : len, this);
1347 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1349 if (movement_occurred) {
1350 _editor->commit_reversible_command ();
1355 RegionCreateDrag::aborted ()
1360 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1368 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1370 Gdk::Cursor* cursor;
1371 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1373 Drag::start_grab (event);
1375 region = &cnote->region_view();
1377 double const region_start = region->get_position_pixels();
1378 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1380 if (grab_x() <= middle_point) {
1381 cursor = _editor->left_side_trim_cursor;
1384 cursor = _editor->right_side_trim_cursor;
1388 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1390 if (event->motion.state & Keyboard::PrimaryModifier) {
1396 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1398 if (ms.size() > 1) {
1399 /* has to be relative, may make no sense otherwise */
1403 /* select this note; if it is already selected, preserve the existing selection,
1404 otherwise make this note the only one selected.
1406 region->note_selected (cnote, cnote->selected ());
1408 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1409 MidiRegionSelection::iterator next;
1412 (*r)->begin_resizing (at_front);
1418 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1420 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1421 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1422 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1427 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1429 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1430 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1431 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1436 NoteResizeDrag::aborted ()
1442 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1448 RegionGainDrag::finished (GdkEvent *, bool)
1454 RegionGainDrag::aborted ()
1459 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1460 : RegionDrag (e, i, p, v)
1461 , _have_transaction (false)
1467 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1470 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1471 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1473 if (tv && tv->is_track()) {
1474 speed = tv->track()->speed();
1477 nframes64_t const region_start = (nframes64_t) (_primary->region()->position() / speed);
1478 nframes64_t const region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1479 nframes64_t const region_length = (nframes64_t) (_primary->region()->length() / speed);
1481 nframes64_t const pf = adjusted_current_frame (event);
1483 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1484 _operation = ContentsTrim;
1485 Drag::start_grab (event, _editor->trimmer_cursor);
1487 /* These will get overridden for a point trim.*/
1488 if (pf < (region_start + region_length/2)) {
1489 /* closer to start */
1490 _operation = StartTrim;
1491 Drag::start_grab (event, _editor->left_side_trim_cursor);
1494 _operation = EndTrim;
1495 Drag::start_grab (event, _editor->right_side_trim_cursor);
1499 switch (_operation) {
1501 _editor->show_verbose_time_cursor (region_start, 10);
1504 _editor->show_verbose_time_cursor (region_end, 10);
1507 _editor->show_verbose_time_cursor (pf, 10);
1513 TrimDrag::motion (GdkEvent* event, bool first_move)
1515 RegionView* rv = _primary;
1517 /* snap modifier works differently here..
1518 its current state has to be passed to the
1519 various trim functions in order to work properly
1523 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1524 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1525 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1527 if (tv && tv->is_track()) {
1528 speed = tv->track()->speed();
1531 nframes64_t const pf = adjusted_current_frame (event);
1537 switch (_operation) {
1539 trim_type = "Region start trim";
1542 trim_type = "Region end trim";
1545 trim_type = "Region content trim";
1549 _editor->begin_reversible_command (trim_type);
1550 _have_transaction = true;
1552 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1553 RegionView* rv = i->view;
1554 rv->fake_set_opaque(false);
1555 rv->enable_display (false);
1556 rv->region()->clear_changes ();
1557 rv->region()->suspend_property_changes ();
1559 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1562 arv->temporarily_hide_envelope ();
1565 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1566 insert_result = _editor->motion_frozen_playlists.insert (pl);
1568 if (insert_result.second) {
1574 bool non_overlap_trim = false;
1576 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1577 non_overlap_trim = true;
1580 switch (_operation) {
1582 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1583 _editor->single_start_trim (*i->view, pf, non_overlap_trim);
1588 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1589 _editor->single_end_trim (*i->view, pf, non_overlap_trim);
1595 bool swap_direction = false;
1597 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1598 swap_direction = true;
1601 nframes64_t frame_delta = 0;
1603 bool left_direction = false;
1604 if (last_pointer_frame() > pf) {
1605 left_direction = true;
1608 if (left_direction) {
1609 frame_delta = (last_pointer_frame() - pf);
1611 frame_delta = (pf - last_pointer_frame());
1614 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1615 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1621 switch (_operation) {
1623 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1626 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1629 _editor->show_verbose_time_cursor (pf, 10);
1636 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1638 if (movement_occurred) {
1639 motion (event, false);
1641 if (!_editor->selection->selected (_primary)) {
1642 _editor->thaw_region_after_trim (*_primary);
1645 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1646 _editor->thaw_region_after_trim (*i->view);
1647 i->view->enable_display (true);
1648 i->view->fake_set_opaque (true);
1649 if (_have_transaction) {
1650 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1654 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1658 _editor->motion_frozen_playlists.clear ();
1660 if (_have_transaction) {
1661 _editor->commit_reversible_command();
1665 /* no mouse movement */
1666 _editor->point_trim (event, adjusted_current_frame (event));
1671 TrimDrag::aborted ()
1673 /* Our motion method is changing model state, so use the Undo system
1674 to cancel. Perhaps not ideal, as this will leave an Undo point
1675 behind which may be slightly odd from the user's point of view.
1680 if (_have_transaction) {
1685 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1689 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1694 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1697 // create a dummy marker for visual representation of moving the copy.
1698 // The actual copying is not done before we reach the finish callback.
1700 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1701 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1702 *new MeterSection (_marker->meter()));
1704 _item = &new_marker->the_item ();
1705 _marker = new_marker;
1709 MetricSection& section (_marker->meter());
1711 if (!section.movable()) {
1717 Drag::start_grab (event, cursor);
1719 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1721 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1725 MeterMarkerDrag::motion (GdkEvent* event, bool)
1727 nframes64_t const pf = adjusted_current_frame (event);
1729 _marker->set_position (pf);
1731 _editor->show_verbose_time_cursor (pf, 10);
1735 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1737 if (!movement_occurred) {
1741 motion (event, false);
1745 TempoMap& map (_editor->session()->tempo_map());
1746 map.bbt_time (last_pointer_frame(), when);
1748 if (_copy == true) {
1749 _editor->begin_reversible_command (_("copy meter mark"));
1750 XMLNode &before = map.get_state();
1751 map.add_meter (_marker->meter(), when);
1752 XMLNode &after = map.get_state();
1753 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1754 _editor->commit_reversible_command ();
1756 // delete the dummy marker we used for visual representation of copying.
1757 // a new visual marker will show up automatically.
1760 _editor->begin_reversible_command (_("move meter mark"));
1761 XMLNode &before = map.get_state();
1762 map.move_meter (_marker->meter(), when);
1763 XMLNode &after = map.get_state();
1764 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1765 _editor->commit_reversible_command ();
1770 MeterMarkerDrag::aborted ()
1772 _marker->set_position (_marker->meter().frame ());
1775 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1779 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1784 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1789 // create a dummy marker for visual representation of moving the copy.
1790 // The actual copying is not done before we reach the finish callback.
1792 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1793 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
1794 *new TempoSection (_marker->tempo()));
1796 _item = &new_marker->the_item ();
1797 _marker = new_marker;
1801 MetricSection& section (_marker->tempo());
1803 if (!section.movable()) {
1808 Drag::start_grab (event, cursor);
1810 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1811 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1815 TempoMarkerDrag::motion (GdkEvent* event, bool)
1817 nframes64_t const pf = adjusted_current_frame (event);
1818 _marker->set_position (pf);
1819 _editor->show_verbose_time_cursor (pf, 10);
1823 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1825 if (!movement_occurred) {
1829 motion (event, false);
1833 TempoMap& map (_editor->session()->tempo_map());
1834 map.bbt_time (last_pointer_frame(), when);
1836 if (_copy == true) {
1837 _editor->begin_reversible_command (_("copy tempo mark"));
1838 XMLNode &before = map.get_state();
1839 map.add_tempo (_marker->tempo(), when);
1840 XMLNode &after = map.get_state();
1841 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1842 _editor->commit_reversible_command ();
1844 // delete the dummy marker we used for visual representation of copying.
1845 // a new visual marker will show up automatically.
1848 _editor->begin_reversible_command (_("move tempo mark"));
1849 XMLNode &before = map.get_state();
1850 map.move_tempo (_marker->tempo(), when);
1851 XMLNode &after = map.get_state();
1852 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1853 _editor->commit_reversible_command ();
1858 TempoMarkerDrag::aborted ()
1860 _marker->set_position (_marker->tempo().frame());
1863 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1867 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1872 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1874 Drag::start_grab (event, c);
1878 nframes64_t where = _editor->event_frame (event, 0, 0);
1880 _editor->snap_to_with_modifier (where, event);
1881 _editor->playhead_cursor->set_position (where);
1885 if (_cursor == _editor->playhead_cursor) {
1886 _editor->_dragging_playhead = true;
1888 Session* s = _editor->session ();
1891 if (_was_rolling && _stop) {
1895 if (s->is_auditioning()) {
1896 s->cancel_audition ();
1899 s->request_suspend_timecode_transmission ();
1901 if (s->timecode_transmission_suspended ()) {
1902 nframes64_t const f = _editor->playhead_cursor->current_frame;
1903 s->send_mmc_locate (f);
1904 s->send_full_time_code (f);
1909 _pointer_frame_offset = raw_grab_frame() - _cursor->current_frame;
1911 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1915 CursorDrag::motion (GdkEvent* event, bool)
1917 nframes64_t const adjusted_frame = adjusted_current_frame (event);
1919 if (adjusted_frame == last_pointer_frame()) {
1923 _cursor->set_position (adjusted_frame);
1925 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1927 Session* s = _editor->session ();
1928 if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
1929 nframes64_t const f = _editor->playhead_cursor->current_frame;
1930 s->send_mmc_locate (f);
1931 s->send_full_time_code (f);
1936 _editor->update_canvas_now ();
1938 _editor->UpdateAllTransportClocks (_cursor->current_frame);
1942 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
1944 _editor->_dragging_playhead = false;
1946 if (!movement_occurred && _stop) {
1950 motion (event, false);
1952 if (_item == &_editor->playhead_cursor->canvas_item) {
1953 Session* s = _editor->session ();
1955 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
1956 _editor->_pending_locate_request = true;
1957 s->request_resume_timecode_transmission ();
1963 CursorDrag::aborted ()
1965 if (_editor->_dragging_playhead) {
1966 _editor->session()->request_resume_timecode_transmission ();
1967 _editor->_dragging_playhead = false;
1970 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
1973 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1974 : RegionDrag (e, i, p, v)
1980 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1982 Drag::start_grab (event, cursor);
1984 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
1985 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
1987 _pointer_frame_offset = raw_grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
1988 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
1990 arv->show_fade_line((nframes64_t) r->fade_in()->back()->when);
1994 FadeInDrag::motion (GdkEvent* event, bool)
1996 nframes64_t fade_length;
1998 nframes64_t const pos = adjusted_current_frame (event);
2000 boost::shared_ptr<Region> region = _primary->region ();
2002 if (pos < (region->position() + 64)) {
2003 fade_length = 64; // this should be a minimum defined somewhere
2004 } else if (pos > region->last_frame()) {
2005 fade_length = region->length();
2007 fade_length = pos - region->position();
2010 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2012 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2018 tmp->reset_fade_in_shape_width (fade_length);
2019 tmp->show_fade_line((nframes64_t) fade_length);
2022 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2026 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2028 if (!movement_occurred) {
2032 nframes64_t fade_length;
2034 nframes64_t const pos = adjusted_current_frame (event);
2036 boost::shared_ptr<Region> region = _primary->region ();
2038 if (pos < (region->position() + 64)) {
2039 fade_length = 64; // this should be a minimum defined somewhere
2040 } else if (pos > region->last_frame()) {
2041 fade_length = region->length();
2043 fade_length = pos - region->position();
2046 _editor->begin_reversible_command (_("change fade in length"));
2048 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2050 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2056 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2057 XMLNode &before = alist->get_state();
2059 tmp->audio_region()->set_fade_in_length (fade_length);
2060 tmp->audio_region()->set_fade_in_active (true);
2061 tmp->hide_fade_line();
2063 XMLNode &after = alist->get_state();
2064 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2067 _editor->commit_reversible_command ();
2071 FadeInDrag::aborted ()
2073 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2074 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2080 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2081 tmp->hide_fade_line();
2085 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2086 : RegionDrag (e, i, p, v)
2092 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2094 Drag::start_grab (event, cursor);
2096 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2097 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2099 _pointer_frame_offset = raw_grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2100 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2102 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2106 FadeOutDrag::motion (GdkEvent* event, bool)
2108 nframes64_t fade_length;
2110 nframes64_t const pos = adjusted_current_frame (event);
2112 boost::shared_ptr<Region> region = _primary->region ();
2114 if (pos > (region->last_frame() - 64)) {
2115 fade_length = 64; // this should really be a minimum fade defined somewhere
2117 else if (pos < region->position()) {
2118 fade_length = region->length();
2121 fade_length = region->last_frame() - pos;
2124 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2126 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2132 tmp->reset_fade_out_shape_width (fade_length);
2133 tmp->show_fade_line(region->length() - fade_length);
2136 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2140 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2142 if (!movement_occurred) {
2146 nframes64_t fade_length;
2148 nframes64_t const pos = adjusted_current_frame (event);
2150 boost::shared_ptr<Region> region = _primary->region ();
2152 if (pos > (region->last_frame() - 64)) {
2153 fade_length = 64; // this should really be a minimum fade defined somewhere
2155 else if (pos < region->position()) {
2156 fade_length = region->length();
2159 fade_length = region->last_frame() - pos;
2162 _editor->begin_reversible_command (_("change fade out length"));
2164 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2166 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2172 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2173 XMLNode &before = alist->get_state();
2175 tmp->audio_region()->set_fade_out_length (fade_length);
2176 tmp->audio_region()->set_fade_out_active (true);
2177 tmp->hide_fade_line();
2179 XMLNode &after = alist->get_state();
2180 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2183 _editor->commit_reversible_command ();
2187 FadeOutDrag::aborted ()
2189 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2190 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2196 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2197 tmp->hide_fade_line();
2201 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2204 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2207 _points.push_back (Gnome::Art::Point (0, 0));
2208 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2210 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2211 _line->property_width_pixels() = 1;
2212 _line->property_points () = _points;
2215 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2218 MarkerDrag::~MarkerDrag ()
2220 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2226 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2228 Drag::start_grab (event, cursor);
2232 Location *location = _editor->find_location_from_marker (_marker, is_start);
2233 _editor->_dragging_edit_point = true;
2235 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2237 update_item (location);
2239 // _drag_line->show();
2240 // _line->raise_to_top();
2243 _editor->show_verbose_time_cursor (location->start(), 10);
2245 _editor->show_verbose_time_cursor (location->end(), 10);
2248 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2251 case Selection::Toggle:
2252 _editor->selection->toggle (_marker);
2254 case Selection::Set:
2255 if (!_editor->selection->selected (_marker)) {
2256 _editor->selection->set (_marker);
2259 case Selection::Extend:
2261 Locations::LocationList ll;
2262 list<Marker*> to_add;
2264 _editor->selection->markers.range (s, e);
2265 s = min (_marker->position(), s);
2266 e = max (_marker->position(), e);
2269 if (e < max_frames) {
2272 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2273 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2274 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2277 to_add.push_back (lm->start);
2280 to_add.push_back (lm->end);
2284 if (!to_add.empty()) {
2285 _editor->selection->add (to_add);
2289 case Selection::Add:
2290 _editor->selection->add (_marker);
2294 /* Set up copies for us to manipulate during the drag */
2296 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2297 Location* l = _editor->find_location_from_marker (*i, is_start);
2298 _copied_locations.push_back (new Location (*l));
2303 MarkerDrag::motion (GdkEvent* event, bool)
2305 nframes64_t f_delta = 0;
2307 bool move_both = false;
2309 Location *real_location;
2310 Location *copy_location = 0;
2312 nframes64_t const newframe = adjusted_current_frame (event);
2314 nframes64_t next = newframe;
2316 if (newframe == last_pointer_frame()) {
2320 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2324 MarkerSelection::iterator i;
2325 list<Location*>::iterator x;
2327 /* find the marker we're dragging, and compute the delta */
2329 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2330 x != _copied_locations.end() && i != _editor->selection->markers.end();
2336 if (marker == _marker) {
2338 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2343 if (real_location->is_mark()) {
2344 f_delta = newframe - copy_location->start();
2348 switch (marker->type()) {
2350 case Marker::LoopStart:
2351 case Marker::PunchIn:
2352 f_delta = newframe - copy_location->start();
2356 case Marker::LoopEnd:
2357 case Marker::PunchOut:
2358 f_delta = newframe - copy_location->end();
2361 /* what kind of marker is this ? */
2369 if (i == _editor->selection->markers.end()) {
2370 /* hmm, impossible - we didn't find the dragged marker */
2374 /* now move them all */
2376 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2377 x != _copied_locations.end() && i != _editor->selection->markers.end();
2383 /* call this to find out if its the start or end */
2385 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2389 if (real_location->locked()) {
2393 if (copy_location->is_mark()) {
2397 copy_location->set_start (copy_location->start() + f_delta);
2401 nframes64_t new_start = copy_location->start() + f_delta;
2402 nframes64_t new_end = copy_location->end() + f_delta;
2404 if (is_start) { // start-of-range marker
2407 copy_location->set_start (new_start);
2408 copy_location->set_end (new_end);
2409 } else if (new_start < copy_location->end()) {
2410 copy_location->set_start (new_start);
2412 _editor->snap_to (next, 1, true);
2413 copy_location->set_end (next);
2414 copy_location->set_start (newframe);
2417 } else { // end marker
2420 copy_location->set_end (new_end);
2421 copy_location->set_start (new_start);
2422 } else if (new_end > copy_location->start()) {
2423 copy_location->set_end (new_end);
2424 } else if (newframe > 0) {
2425 _editor->snap_to (next, -1, true);
2426 copy_location->set_start (next);
2427 copy_location->set_end (newframe);
2432 update_item (copy_location);
2434 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2437 lm->set_position (copy_location->start(), copy_location->end());
2441 assert (!_copied_locations.empty());
2443 _editor->show_verbose_time_cursor (newframe, 10);
2446 _editor->update_canvas_now ();
2451 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2453 if (!movement_occurred) {
2455 /* just a click, do nothing but finish
2456 off the selection process
2459 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2462 case Selection::Set:
2463 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2464 _editor->selection->set (_marker);
2468 case Selection::Toggle:
2469 case Selection::Extend:
2470 case Selection::Add:
2477 _editor->_dragging_edit_point = false;
2479 _editor->begin_reversible_command ( _("move marker") );
2480 XMLNode &before = _editor->session()->locations()->get_state();
2482 MarkerSelection::iterator i;
2483 list<Location*>::iterator x;
2486 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2487 x != _copied_locations.end() && i != _editor->selection->markers.end();
2490 Location * location = _editor->find_location_from_marker (*i, is_start);
2494 if (location->locked()) {
2498 if (location->is_mark()) {
2499 location->set_start ((*x)->start());
2501 location->set ((*x)->start(), (*x)->end());
2506 XMLNode &after = _editor->session()->locations()->get_state();
2507 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2508 _editor->commit_reversible_command ();
2514 MarkerDrag::aborted ()
2520 MarkerDrag::update_item (Location* location)
2522 double const x1 = _editor->frame_to_pixel (location->start());
2524 _points.front().set_x(x1);
2525 _points.back().set_x(x1);
2526 _line->property_points() = _points;
2529 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2531 _cumulative_x_drag (0),
2532 _cumulative_y_drag (0)
2534 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2540 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2542 Drag::start_grab (event, _editor->fader_cursor);
2544 // start the grab at the center of the control point so
2545 // the point doesn't 'jump' to the mouse after the first drag
2546 _fixed_grab_x = _point->get_x();
2547 _fixed_grab_y = _point->get_y();
2549 float const fraction = 1 - (_point->get_y() / _point->line().height());
2551 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2553 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2554 event->button.x + 10, event->button.y + 10);
2556 _editor->show_verbose_canvas_cursor ();
2560 ControlPointDrag::motion (GdkEvent* event, bool)
2562 double dx = _drags->current_pointer_x() - last_pointer_x();
2563 double dy = _drags->current_pointer_y() - last_pointer_y();
2565 if (event->button.state & Keyboard::SecondaryModifier) {
2570 /* coordinate in pixels relative to the start of the region (for region-based automation)
2571 or track (for track-based automation) */
2572 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2573 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2575 // calculate zero crossing point. back off by .01 to stay on the
2576 // positive side of zero
2577 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2579 // make sure we hit zero when passing through
2580 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2584 if (_x_constrained) {
2587 if (_y_constrained) {
2591 _cumulative_x_drag = cx - _fixed_grab_x;
2592 _cumulative_y_drag = cy - _fixed_grab_y;
2596 cy = min ((double) _point->line().height(), cy);
2598 framepos_t cx_frames = _editor->unit_to_frame (cx);
2600 if (!_x_constrained) {
2601 _editor->snap_to_with_modifier (cx_frames, event);
2604 cx_frames = min (cx_frames, _point->line().maximum_time());
2606 float const fraction = 1.0 - (cy / _point->line().height());
2608 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2610 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2612 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2616 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2618 if (!movement_occurred) {
2622 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2623 _editor->reset_point_selection ();
2627 motion (event, false);
2629 _point->line().end_drag ();
2633 ControlPointDrag::aborted ()
2635 _point->line().reset ();
2639 ControlPointDrag::active (Editing::MouseMode m)
2641 if (m == Editing::MouseGain) {
2642 /* always active in mouse gain */
2646 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2647 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2650 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2653 _cumulative_y_drag (0)
2658 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2660 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2663 _item = &_line->grab_item ();
2665 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2666 origin, and ditto for y.
2669 double cx = event->button.x;
2670 double cy = event->button.y;
2672 _line->parent_group().w2i (cx, cy);
2674 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2679 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2680 /* no adjacent points */
2684 Drag::start_grab (event, _editor->fader_cursor);
2686 /* store grab start in parent frame */
2691 double fraction = 1.0 - (cy / _line->height());
2693 _line->start_drag_line (before, after, fraction);
2695 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2696 event->button.x + 10, event->button.y + 10);
2698 _editor->show_verbose_canvas_cursor ();
2702 LineDrag::motion (GdkEvent* event, bool)
2704 double dy = _drags->current_pointer_y() - last_pointer_y();
2706 if (event->button.state & Keyboard::SecondaryModifier) {
2710 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2712 _cumulative_y_drag = cy - _fixed_grab_y;
2715 cy = min ((double) _line->height(), cy);
2717 double const fraction = 1.0 - (cy / _line->height());
2721 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2727 /* we are ignoring x position for this drag, so we can just pass in anything */
2728 _line->drag_motion (0, fraction, true, push);
2730 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2734 LineDrag::finished (GdkEvent* event, bool)
2736 motion (event, false);
2741 LineDrag::aborted ()
2746 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2749 _cumulative_x_drag (0)
2754 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2756 Drag::start_grab (event);
2758 _line = reinterpret_cast<SimpleLine*> (_item);
2761 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2763 double cx = event->button.x;
2764 double cy = event->button.y;
2766 _item->property_parent().get_value()->w2i(cx, cy);
2768 /* store grab start in parent frame */
2769 _region_view_grab_x = cx;
2771 _before = _line->property_x1();
2773 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2775 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2779 FeatureLineDrag::motion (GdkEvent* event, bool)
2781 double dx = _drags->current_pointer_x() - last_pointer_x();
2783 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2785 _cumulative_x_drag += dx;
2787 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2796 _line->property_x1() = cx;
2797 _line->property_x2() = cx;
2799 _before = _line->property_x1();
2803 FeatureLineDrag::finished (GdkEvent* event, bool)
2805 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2806 _arv->update_transient(_before, _line->property_x1());
2810 FeatureLineDrag::aborted ()
2816 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2818 Drag::start_grab (event);
2819 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2823 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2830 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2832 nframes64_t grab = grab_frame ();
2833 if (Config->get_rubberbanding_snaps_to_grid ()) {
2834 _editor->snap_to_with_modifier (grab, event);
2837 /* base start and end on initial click position */
2847 if (_drags->current_pointer_y() < grab_y()) {
2848 y1 = _drags->current_pointer_y();
2851 y2 = _drags->current_pointer_y();
2856 if (start != end || y1 != y2) {
2858 double x1 = _editor->frame_to_pixel (start);
2859 double x2 = _editor->frame_to_pixel (end);
2861 _editor->rubberband_rect->property_x1() = x1;
2862 _editor->rubberband_rect->property_y1() = y1;
2863 _editor->rubberband_rect->property_x2() = x2;
2864 _editor->rubberband_rect->property_y2() = y2;
2866 _editor->rubberband_rect->show();
2867 _editor->rubberband_rect->raise_to_top();
2869 _editor->show_verbose_time_cursor (pf, 10);
2874 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2876 if (movement_occurred) {
2878 motion (event, false);
2881 if (_drags->current_pointer_y() < grab_y()) {
2882 y1 = _drags->current_pointer_y();
2885 y2 = _drags->current_pointer_y();
2890 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2893 _editor->begin_reversible_command (_("rubberband selection"));
2895 if (grab_frame() < last_pointer_frame()) {
2896 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
2898 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
2902 _editor->commit_reversible_command ();
2906 if (!getenv("ARDOUR_SAE")) {
2907 _editor->selection->clear_tracks();
2909 _editor->selection->clear_regions();
2910 _editor->selection->clear_points ();
2911 _editor->selection->clear_lines ();
2914 _editor->rubberband_rect->hide();
2918 RubberbandSelectDrag::aborted ()
2920 _editor->rubberband_rect->hide ();
2924 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2926 Drag::start_grab (event, cursor);
2928 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2932 TimeFXDrag::motion (GdkEvent* event, bool)
2934 RegionView* rv = _primary;
2936 nframes64_t const pf = adjusted_current_frame (event);
2938 if (pf > rv->region()->position()) {
2939 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
2942 _editor->show_verbose_time_cursor (pf, 10);
2946 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2948 _primary->get_time_axis_view().hide_timestretch ();
2950 if (!movement_occurred) {
2954 if (last_pointer_frame() < _primary->region()->position()) {
2955 /* backwards drag of the left edge - not usable */
2959 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
2961 float percentage = (double) newlen / (double) _primary->region()->length();
2963 #ifndef USE_RUBBERBAND
2964 // Soundtouch uses percentage / 100 instead of normal (/ 1)
2965 if (_primary->region()->data_type() == DataType::AUDIO) {
2966 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
2970 _editor->begin_reversible_command (_("timestretch"));
2972 // XXX how do timeFX on multiple regions ?
2977 if (_editor->time_stretch (rs, percentage) == -1) {
2978 error << _("An error occurred while executing time stretch operation") << endmsg;
2983 TimeFXDrag::aborted ()
2985 _primary->get_time_axis_view().hide_timestretch ();
2990 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2992 Drag::start_grab (event);
2996 ScrubDrag::motion (GdkEvent* /*event*/, bool)
2998 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3002 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3004 if (movement_occurred && _editor->session()) {
3005 /* make sure we stop */
3006 _editor->session()->request_transport_speed (0.0);
3011 ScrubDrag::aborted ()
3016 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3020 , _original_pointer_time_axis (-1)
3021 , _last_pointer_time_axis (-1)
3027 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3029 nframes64_t start = 0;
3030 nframes64_t end = 0;
3032 if (_editor->session() == 0) {
3036 Gdk::Cursor* cursor = 0;
3038 switch (_operation) {
3039 case CreateSelection:
3040 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3045 cursor = _editor->selector_cursor;
3046 Drag::start_grab (event, cursor);
3049 case SelectionStartTrim:
3050 if (_editor->clicked_axisview) {
3051 _editor->clicked_axisview->order_selection_trims (_item, true);
3053 Drag::start_grab (event, _editor->left_side_trim_cursor);
3054 start = _editor->selection->time[_editor->clicked_selection].start;
3055 _pointer_frame_offset = raw_grab_frame() - start;
3058 case SelectionEndTrim:
3059 if (_editor->clicked_axisview) {
3060 _editor->clicked_axisview->order_selection_trims (_item, false);
3062 Drag::start_grab (event, _editor->right_side_trim_cursor);
3063 end = _editor->selection->time[_editor->clicked_selection].end;
3064 _pointer_frame_offset = raw_grab_frame() - end;
3068 start = _editor->selection->time[_editor->clicked_selection].start;
3069 Drag::start_grab (event, cursor);
3070 _pointer_frame_offset = raw_grab_frame() - start;
3074 if (_operation == SelectionMove) {
3075 _editor->show_verbose_time_cursor (start, 10);
3077 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3080 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3084 SelectionDrag::motion (GdkEvent* event, bool first_move)
3086 nframes64_t start = 0;
3087 nframes64_t end = 0;
3090 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3091 if (pending_time_axis.first == 0) {
3095 nframes64_t const pending_position = adjusted_current_frame (event);
3097 /* only alter selection if things have changed */
3099 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3103 switch (_operation) {
3104 case CreateSelection:
3106 nframes64_t grab = grab_frame ();
3109 _editor->snap_to (grab);
3112 if (pending_position < grab_frame()) {
3113 start = pending_position;
3116 end = pending_position;
3120 /* first drag: Either add to the selection
3121 or create a new selection
3127 /* adding to the selection */
3128 _editor->set_selected_track_as_side_effect (Selection::Add);
3129 //_editor->selection->add (_editor->clicked_axisview);
3130 _editor->clicked_selection = _editor->selection->add (start, end);
3135 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3136 //_editor->selection->set (_editor->clicked_axisview);
3137 _editor->set_selected_track_as_side_effect (Selection::Set);
3140 _editor->clicked_selection = _editor->selection->set (start, end);
3144 /* select the track that we're in */
3145 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3146 // _editor->set_selected_track_as_side_effect (Selection::Add);
3147 _editor->selection->add (pending_time_axis.first);
3148 _added_time_axes.push_back (pending_time_axis.first);
3151 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3152 tracks that we selected in the first place.
3155 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3156 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3158 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3159 while (i != _added_time_axes.end()) {
3161 list<TimeAxisView*>::iterator tmp = i;
3164 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3165 _editor->selection->remove (*i);
3166 _added_time_axes.remove (*i);
3175 case SelectionStartTrim:
3177 start = _editor->selection->time[_editor->clicked_selection].start;
3178 end = _editor->selection->time[_editor->clicked_selection].end;
3180 if (pending_position > end) {
3183 start = pending_position;
3187 case SelectionEndTrim:
3189 start = _editor->selection->time[_editor->clicked_selection].start;
3190 end = _editor->selection->time[_editor->clicked_selection].end;
3192 if (pending_position < start) {
3195 end = pending_position;
3202 start = _editor->selection->time[_editor->clicked_selection].start;
3203 end = _editor->selection->time[_editor->clicked_selection].end;
3205 length = end - start;
3207 start = pending_position;
3208 _editor->snap_to (start);
3210 end = start + length;
3215 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3216 _editor->start_canvas_autoscroll (1, 0);
3220 _editor->selection->replace (_editor->clicked_selection, start, end);
3223 if (_operation == SelectionMove) {
3224 _editor->show_verbose_time_cursor(start, 10);
3226 _editor->show_verbose_time_cursor(pending_position, 10);
3231 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3233 Session* s = _editor->session();
3235 if (movement_occurred) {
3236 motion (event, false);
3237 /* XXX this is not object-oriented programming at all. ick */
3238 if (_editor->selection->time.consolidate()) {
3239 _editor->selection->TimeChanged ();
3242 /* XXX what if its a music time selection? */
3243 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3244 s->request_play_range (&_editor->selection->time, true);
3249 /* just a click, no pointer movement.*/
3251 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3252 _editor->selection->clear_time();
3255 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3256 _editor->selection->set (_editor->clicked_axisview);
3259 if (s && s->get_play_range () && s->transport_rolling()) {
3260 s->request_stop (false, false);
3265 _editor->stop_canvas_autoscroll ();
3269 SelectionDrag::aborted ()
3274 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3279 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3280 physical_screen_height (_editor->get_window()));
3281 _drag_rect->hide ();
3283 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3284 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3288 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3290 if (_editor->session() == 0) {
3294 Gdk::Cursor* cursor = 0;
3296 if (!_editor->temp_location) {
3297 _editor->temp_location = new Location (*_editor->session());
3300 switch (_operation) {
3301 case CreateRangeMarker:
3302 case CreateTransportMarker:
3303 case CreateCDMarker:
3305 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3310 cursor = _editor->selector_cursor;
3314 Drag::start_grab (event, cursor);
3316 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3320 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3322 nframes64_t start = 0;
3323 nframes64_t end = 0;
3324 ArdourCanvas::SimpleRect *crect;
3326 switch (_operation) {
3327 case CreateRangeMarker:
3328 crect = _editor->range_bar_drag_rect;
3330 case CreateTransportMarker:
3331 crect = _editor->transport_bar_drag_rect;
3333 case CreateCDMarker:
3334 crect = _editor->cd_marker_bar_drag_rect;
3337 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3342 nframes64_t const pf = adjusted_current_frame (event);
3344 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3345 nframes64_t grab = grab_frame ();
3346 _editor->snap_to (grab);
3348 if (pf < grab_frame()) {
3356 /* first drag: Either add to the selection
3357 or create a new selection.
3362 _editor->temp_location->set (start, end);
3366 update_item (_editor->temp_location);
3368 //_drag_rect->raise_to_top();
3373 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3374 _editor->start_canvas_autoscroll (1, 0);
3378 _editor->temp_location->set (start, end);
3380 double x1 = _editor->frame_to_pixel (start);
3381 double x2 = _editor->frame_to_pixel (end);
3382 crect->property_x1() = x1;
3383 crect->property_x2() = x2;
3385 update_item (_editor->temp_location);
3388 _editor->show_verbose_time_cursor (pf, 10);
3393 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3395 Location * newloc = 0;
3399 if (movement_occurred) {
3400 motion (event, false);
3403 switch (_operation) {
3404 case CreateRangeMarker:
3405 case CreateCDMarker:
3407 _editor->begin_reversible_command (_("new range marker"));
3408 XMLNode &before = _editor->session()->locations()->get_state();
3409 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3410 if (_operation == CreateCDMarker) {
3411 flags = Location::IsRangeMarker | Location::IsCDMarker;
3412 _editor->cd_marker_bar_drag_rect->hide();
3415 flags = Location::IsRangeMarker;
3416 _editor->range_bar_drag_rect->hide();
3418 newloc = new Location (
3419 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3422 _editor->session()->locations()->add (newloc, true);
3423 XMLNode &after = _editor->session()->locations()->get_state();
3424 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3425 _editor->commit_reversible_command ();
3429 case CreateTransportMarker:
3430 // popup menu to pick loop or punch
3431 _editor->new_transport_marker_context_menu (&event->button, _item);
3435 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3437 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3442 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3444 if (end == max_frames) {
3445 end = _editor->session()->current_end_frame ();
3448 if (start == max_frames) {
3449 start = _editor->session()->current_start_frame ();
3452 switch (_editor->mouse_mode) {
3454 /* find the two markers on either side and then make the selection from it */
3455 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3459 /* find the two markers on either side of the click and make the range out of it */
3460 _editor->selection->set (start, end);
3469 _editor->stop_canvas_autoscroll ();
3473 RangeMarkerBarDrag::aborted ()
3479 RangeMarkerBarDrag::update_item (Location* location)
3481 double const x1 = _editor->frame_to_pixel (location->start());
3482 double const x2 = _editor->frame_to_pixel (location->end());
3484 _drag_rect->property_x1() = x1;
3485 _drag_rect->property_x2() = x2;
3489 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3491 Drag::start_grab (event, _editor->zoom_cursor);
3492 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3496 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3501 nframes64_t const pf = adjusted_current_frame (event);
3503 nframes64_t grab = grab_frame ();
3504 _editor->snap_to_with_modifier (grab, event);
3506 /* base start and end on initial click position */
3518 _editor->zoom_rect->show();
3519 _editor->zoom_rect->raise_to_top();
3522 _editor->reposition_zoom_rect(start, end);
3524 _editor->show_verbose_time_cursor (pf, 10);
3529 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3531 if (movement_occurred) {
3532 motion (event, false);
3534 if (grab_frame() < last_pointer_frame()) {
3535 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3537 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3540 _editor->temporal_zoom_to_frame (false, grab_frame());
3542 temporal_zoom_step (false);
3543 center_screen (grab_frame());
3547 _editor->zoom_rect->hide();
3551 MouseZoomDrag::aborted ()
3553 _editor->zoom_rect->hide ();
3556 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3558 , _cumulative_dx (0)
3559 , _cumulative_dy (0)
3561 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3562 _region = &_primary->region_view ();
3563 _note_height = _region->midi_stream_view()->note_height ();
3567 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3569 Drag::start_grab (event);
3571 if (!(_was_selected = _primary->selected())) {
3573 /* tertiary-click means extend selection - we'll do that on button release,
3574 so don't add it here, because otherwise we make it hard to figure
3575 out the "extend-to" range.
3578 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3581 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3584 _region->note_selected (_primary, true);
3586 _region->unique_select (_primary);
3592 /** @return Current total drag x change in frames */
3594 NoteDrag::total_dx () const
3597 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3599 /* primary note time */
3600 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3602 /* new time of the primary note relative to the region position */
3603 frameoffset_t const st = n + dx;
3605 /* snap and return corresponding delta */
3606 return _region->snap_frame_to_frame (st) - n;
3609 /** @return Current total drag y change in notes */
3611 NoteDrag::total_dy () const
3613 /* this is `backwards' to make increasing note number go in the right direction */
3614 double const dy = _drags->current_pointer_y() - grab_y();
3619 if (abs (dy) >= _note_height) {
3621 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3623 ndy = (int8_t) floor (dy / _note_height / 2.0);
3632 NoteDrag::motion (GdkEvent *, bool)
3634 /* Total change in x and y since the start of the drag */
3635 frameoffset_t const dx = total_dx ();
3636 int8_t const dy = total_dy ();
3638 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3639 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3640 double const tdy = dy * _note_height - _cumulative_dy;
3643 _region->move_selection (tdx, tdy);
3644 _cumulative_dx += tdx;
3645 _cumulative_dy += tdy;
3648 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + dy).c_str(),
3649 (int) floor (_primary->note()->note() + dy));
3651 _editor->show_verbose_canvas_cursor_with (buf);
3656 NoteDrag::finished (GdkEvent* ev, bool moved)
3659 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3661 if (_was_selected) {
3662 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3664 _region->note_deselected (_primary);
3667 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3668 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3670 if (!extend && !add && _region->selection_size() > 1) {
3671 _region->unique_select (_primary);
3672 } else if (extend) {
3673 _region->note_selected (_primary, true, true);
3675 /* it was added during button press */
3680 _region->note_dropped (_primary, total_dx(), - total_dy());
3685 NoteDrag::aborted ()
3690 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3693 , _nothing_to_drag (false)
3695 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3698 _line = _atav->line ();
3702 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3704 Drag::start_grab (event, cursor);
3706 list<ControlPoint*> points;
3708 XMLNode* state = &_line->get_state ();
3710 if (_ranges.empty()) {
3712 uint32_t const N = _line->npoints ();
3713 for (uint32_t i = 0; i < N; ++i) {
3714 points.push_back (_line->nth (i));
3719 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3720 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3722 /* fade into and out of the region that we're dragging;
3723 64 samples length plucked out of thin air.
3725 nframes64_t const h = (j->start + j->end) / 2;
3726 nframes64_t a = j->start + 64;
3730 nframes64_t b = j->end - 64;
3735 the_list->add (j->start, the_list->eval (j->start));
3736 _line->add_always_in_view (j->start);
3737 the_list->add (a, the_list->eval (a));
3738 _line->add_always_in_view (a);
3739 the_list->add (b, the_list->eval (b));
3740 _line->add_always_in_view (b);
3741 the_list->add (j->end, the_list->eval (j->end));
3742 _line->add_always_in_view (j->end);
3745 uint32_t const N = _line->npoints ();
3746 for (uint32_t i = 0; i < N; ++i) {
3748 ControlPoint* p = _line->nth (i);
3750 list<AudioRange>::const_iterator j = _ranges.begin ();
3751 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3755 if (j != _ranges.end()) {
3756 points.push_back (p);
3761 if (points.empty()) {
3762 _nothing_to_drag = true;
3766 _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
3770 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3772 if (_nothing_to_drag) {
3776 float const f = 1 - (_drags->current_pointer_y() / _line->height());
3778 /* we are ignoring x position for this drag, so we can just pass in anything */
3779 _line->drag_motion (0, f, true, false);
3783 AutomationRangeDrag::finished (GdkEvent* event, bool)
3785 if (_nothing_to_drag) {
3789 motion (event, false);
3791 _line->clear_always_in_view ();
3795 AutomationRangeDrag::aborted ()
3797 _line->clear_always_in_view ();
3801 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
3804 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
3805 layer = v->region()->layer ();
3806 initial_y = v->get_canvas_group()->property_y ();
3807 initial_playlist = v->region()->playlist ();