2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #define __STDC_LIMIT_MACROS 1
22 #include "pbd/memento_command.h"
23 #include "pbd/basename.h"
24 #include "pbd/stateful_diff_command.h"
25 #include "ardour/session.h"
26 #include "ardour/dB.h"
27 #include "ardour/region_factory.h"
31 #include "audio_region_view.h"
32 #include "midi_region_view.h"
33 #include "ardour_ui.h"
34 #include "gui_thread.h"
35 #include "control_point.h"
37 #include "region_gain_line.h"
38 #include "editor_drag.h"
39 #include "audio_time_axis.h"
40 #include "midi_time_axis.h"
41 #include "canvas-note.h"
42 #include "selection.h"
43 #include "midi_selection.h"
44 #include "automation_time_axis.h"
48 using namespace ARDOUR;
51 using namespace Editing;
52 using namespace ArdourCanvas;
54 using Gtkmm2ext::Keyboard;
56 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
58 DragManager::DragManager (Editor* e)
61 , _current_pointer_frame (0)
66 DragManager::~DragManager ()
71 /** Call abort for each active drag */
77 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
88 DragManager::add (Drag* d)
90 d->set_manager (this);
95 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
97 assert (_drags.empty ());
98 d->set_manager (this);
104 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
106 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
108 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
109 (*i)->start_grab (e, c);
113 /** Call end_grab for each active drag.
114 * @return true if any drag reported movement having occurred.
117 DragManager::end_grab (GdkEvent* e)
122 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
123 bool const t = (*i)->end_grab (e);
138 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
142 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
144 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
145 bool const t = (*i)->motion_handler (e, from_autoscroll);
156 DragManager::have_item (ArdourCanvas::Item* i) const
158 list<Drag*>::const_iterator j = _drags.begin ();
159 while (j != _drags.end() && (*j)->item () != i) {
163 return j != _drags.end ();
166 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
169 , _pointer_frame_offset (0)
170 , _move_threshold_passed (false)
171 , _raw_grab_frame (0)
173 , _last_pointer_frame (0)
179 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
185 cursor = _editor->which_grabber_cursor ();
188 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
192 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
195 cursor = _editor->which_grabber_cursor ();
198 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
200 if (Keyboard::is_button2_event (&event->button)) {
201 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
202 _y_constrained = true;
203 _x_constrained = false;
205 _y_constrained = false;
206 _x_constrained = true;
209 _x_constrained = false;
210 _y_constrained = false;
213 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
214 _grab_frame = adjusted_frame (_raw_grab_frame, event);
215 _last_pointer_frame = _grab_frame;
216 _last_pointer_x = _grab_x;
217 _last_pointer_y = _grab_y;
219 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
223 if (_editor->session() && _editor->session()->transport_rolling()) {
226 _was_rolling = false;
229 switch (_editor->snap_type()) {
230 case SnapToRegionStart:
231 case SnapToRegionEnd:
232 case SnapToRegionSync:
233 case SnapToRegionBoundary:
234 _editor->build_region_boundary_cache ();
241 /** Call to end a drag `successfully'. Ungrabs item and calls
242 * subclass' finished() method.
244 * @param event GDK event, or 0.
245 * @return true if some movement occurred, otherwise false.
248 Drag::end_grab (GdkEvent* event)
250 _editor->stop_canvas_autoscroll ();
252 _item->ungrab (event ? event->button.time : 0);
254 finished (event, _move_threshold_passed);
256 _editor->hide_verbose_canvas_cursor();
258 return _move_threshold_passed;
262 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
266 if (f > _pointer_frame_offset) {
267 pos = f - _pointer_frame_offset;
271 _editor->snap_to_with_modifier (pos, event);
278 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
280 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
284 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
286 /* check to see if we have moved in any way that matters since the last motion event */
287 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
288 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
292 pair<framecnt_t, int> const threshold = move_threshold ();
294 bool const old_move_threshold_passed = _move_threshold_passed;
296 if (!from_autoscroll && !_move_threshold_passed) {
298 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
299 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
301 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
304 if (active (_editor->mouse_mode) && _move_threshold_passed) {
306 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
307 if (!from_autoscroll) {
308 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
311 motion (event, _move_threshold_passed != old_move_threshold_passed);
313 _last_pointer_x = _drags->current_pointer_x ();
314 _last_pointer_y = _drags->current_pointer_y ();
315 _last_pointer_frame = adjusted_current_frame (event);
323 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
333 _editor->stop_canvas_autoscroll ();
334 _editor->hide_verbose_canvas_cursor ();
337 struct EditorOrderTimeAxisViewSorter {
338 bool operator() (TimeAxisView* a, TimeAxisView* b) {
339 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
340 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
342 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
346 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
350 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
352 /* Make a list of non-hidden tracks to refer to during the drag */
354 TrackViewList track_views = _editor->track_views;
355 track_views.sort (EditorOrderTimeAxisViewSorter ());
357 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
358 if (!(*i)->hidden()) {
360 _time_axis_views.push_back (*i);
362 TimeAxisView::Children children_list = (*i)->get_child_list ();
363 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
364 _time_axis_views.push_back (j->get());
369 assert (!v.empty ());
371 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
372 _views.push_back (DraggingView (*i, this));
375 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
379 RegionDrag::region_going_away (RegionView* v)
381 list<DraggingView>::iterator i = _views.begin ();
382 while (i != _views.end() && i->view != v) {
386 if (i != _views.end()) {
391 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
393 RegionDrag::find_time_axis_view (TimeAxisView* t) const
396 int const N = _time_axis_views.size ();
397 while (i < N && _time_axis_views[i] != t) {
408 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
409 : RegionDrag (e, i, p, v),
418 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
420 Drag::start_grab (event, cursor);
422 _editor->show_verbose_time_cursor (_last_frame_position, 10);
424 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
425 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
426 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
430 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
432 /* compute the amount of pointer motion in frames, and where
433 the region would be if we moved it by that much.
435 *pending_region_position = adjusted_current_frame (event);
437 framepos_t sync_frame;
438 framecnt_t sync_offset;
441 sync_offset = _primary->region()->sync_offset (sync_dir);
443 /* we don't handle a sync point that lies before zero.
445 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
447 sync_frame = *pending_region_position + (sync_dir*sync_offset);
449 _editor->snap_to_with_modifier (sync_frame, event);
451 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
454 *pending_region_position = _last_frame_position;
457 if (*pending_region_position > max_framepos - _primary->region()->length()) {
458 *pending_region_position = _last_frame_position;
463 /* in locked edit mode, reverse the usual meaning of _x_constrained */
464 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
466 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
468 /* x movement since last time */
469 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
471 /* total x movement */
472 framecnt_t total_dx = *pending_region_position;
473 if (regions_came_from_canvas()) {
474 total_dx = total_dx - grab_frame () + _pointer_frame_offset;
477 /* check that no regions have gone off the start of the session */
478 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
479 if ((i->view->region()->position() + total_dx) < 0) {
481 *pending_region_position = _last_frame_position;
486 _last_frame_position = *pending_region_position;
493 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
495 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
496 int const n = i->time_axis_view + delta_track;
497 if (n < 0 || n >= int (_time_axis_views.size())) {
498 /* off the top or bottom track */
502 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
503 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
504 /* not a track, or the wrong type */
508 int const l = i->layer + delta_layer;
509 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
510 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
511 If it has, the layers will be munged later anyway, so it's ok.
517 /* all regions being dragged are ok with this change */
522 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
524 /* Find the TimeAxisView that the pointer is now over */
525 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
527 /* Bail early if we're not over a track */
528 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
529 if (!rtv || !rtv->is_track()) {
530 _editor->hide_verbose_canvas_cursor ();
534 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
536 /* Here's the current pointer position in terms of time axis view and layer */
537 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
538 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
540 /* Work out the change in x */
541 framepos_t pending_region_position;
542 double const x_delta = compute_x_delta (event, &pending_region_position);
544 /* Work out the change in y */
545 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
546 int delta_layer = current_pointer_layer - _last_pointer_layer;
548 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
549 /* this y movement is not allowed, so do no y movement this time */
550 delta_time_axis_view = 0;
554 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
555 /* haven't reached next snap point, and we're not switching
556 trackviews nor layers. nothing to do.
561 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
563 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
565 RegionView* rv = i->view;
567 if (rv->region()->locked()) {
573 /* here we are calculating the y distance from the
574 top of the first track view to the top of the region
575 area of the track view that we're working on */
577 /* this x value is just a dummy value so that we have something
582 /* distance from the top of this track view to the region area
583 of our track view is always 1 */
587 /* convert to world coordinates, ie distance from the top of
590 rv->get_canvas_frame()->i2w (ix1, iy1);
592 /* compensate for the ruler section and the vertical scrollbar position */
593 iy1 += _editor->get_trackview_group_vertical_offset ();
595 // hide any dependent views
597 rv->get_time_axis_view().hide_dependent_views (*rv);
600 reparent to a non scrolling group so that we can keep the
601 region selection above all time axis views.
602 reparenting means we have to move the rv as the two
603 parent groups have different coordinates.
606 rv->get_canvas_group()->property_y() = iy1 - 1;
607 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
609 rv->fake_set_opaque (true);
612 /* Work out the change in y position of this region view */
616 /* If we have moved tracks, we'll fudge the layer delta so that the
617 region gets moved back onto layer 0 on its new track; this avoids
618 confusion when dragging regions from non-zero layers onto different
621 int this_delta_layer = delta_layer;
622 if (delta_time_axis_view != 0) {
623 this_delta_layer = - i->layer;
626 /* Move this region to layer 0 on its old track */
627 StreamView* lv = _time_axis_views[i->time_axis_view]->view ();
628 if (lv->layer_display() == Stacked) {
629 y_delta -= (lv->layers() - i->layer - 1) * lv->child_height ();
632 /* Now move it to its right layer on the current track */
633 StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view ();
634 if (cv->layer_display() == Stacked) {
635 y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height ();
639 if (delta_time_axis_view > 0) {
640 for (int j = 0; j < delta_time_axis_view; ++j) {
641 y_delta += _time_axis_views[i->time_axis_view + j]->current_height ();
644 /* start by subtracting the height of the track above where we are now */
645 for (int j = 1; j <= -delta_time_axis_view; ++j) {
646 y_delta -= _time_axis_views[i->time_axis_view - j]->current_height ();
651 rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ());
653 /* Update the DraggingView */
654 i->time_axis_view += delta_time_axis_view;
655 i->layer += this_delta_layer;
658 _editor->mouse_brush_insert_region (rv, pending_region_position);
660 rv->move (x_delta, y_delta);
663 } /* foreach region */
665 _total_x_delta += x_delta;
668 _editor->cursor_group->raise_to_top();
671 if (x_delta != 0 && !_brushing) {
672 _editor->show_verbose_time_cursor (_last_frame_position, 10);
675 _last_pointer_time_axis_view += delta_time_axis_view;
676 _last_pointer_layer += delta_layer;
680 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
682 if (_copy && first_move) {
684 /* duplicate the regionview(s) and region(s) */
686 list<DraggingView> new_regionviews;
688 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
690 RegionView* rv = i->view;
691 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
692 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
694 const boost::shared_ptr<const Region> original = rv->region();
695 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
696 region_copy->set_position (original->position(), this);
700 boost::shared_ptr<AudioRegion> audioregion_copy
701 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
703 nrv = new AudioRegionView (*arv, audioregion_copy);
705 boost::shared_ptr<MidiRegion> midiregion_copy
706 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
707 nrv = new MidiRegionView (*mrv, midiregion_copy);
712 nrv->get_canvas_group()->show ();
713 new_regionviews.push_back (DraggingView (nrv, this));
715 /* swap _primary to the copy */
717 if (rv == _primary) {
721 /* ..and deselect the one we copied */
723 rv->set_selected (false);
726 if (!new_regionviews.empty()) {
728 /* reflect the fact that we are dragging the copies */
730 _views = new_regionviews;
732 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
735 sync the canvas to what we think is its current state
736 without it, the canvas seems to
737 "forget" to update properly after the upcoming reparent()
738 ..only if the mouse is in rapid motion at the time of the grab.
739 something to do with regionview creation taking so long?
741 _editor->update_canvas_now();
745 RegionMotionDrag::motion (event, first_move);
749 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
751 if (!movement_occurred) {
756 /* reverse this here so that we have the correct logic to finalize
760 if (Config->get_edit_mode() == Lock) {
761 _x_constrained = !_x_constrained;
764 assert (!_views.empty ());
766 bool const changed_position = (_last_frame_position != _primary->region()->position());
767 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
768 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
770 _editor->update_canvas_now ();
792 RegionMoveDrag::finished_copy (
793 bool const changed_position,
794 bool const changed_tracks,
795 framecnt_t const drag_delta
798 RegionSelection new_views;
799 PlaylistSet modified_playlists;
800 list<RegionView*> views_to_delete;
803 /* all changes were made during motion event handlers */
805 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
809 _editor->commit_reversible_command ();
813 if (_x_constrained) {
814 _editor->begin_reversible_command (_("fixed time region copy"));
816 _editor->begin_reversible_command (_("region copy"));
819 /* insert the regions into their new playlists */
820 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
822 if (i->view->region()->locked()) {
828 if (changed_position && !_x_constrained) {
829 where = i->view->region()->position() - drag_delta;
831 where = i->view->region()->position();
834 RegionView* new_view = insert_region_into_playlist (
835 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
842 new_views.push_back (new_view);
844 /* we don't need the copied RegionView any more */
845 views_to_delete.push_back (i->view);
848 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
849 because when views are deleted they are automagically removed from _views, which messes
852 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
856 /* If we've created new regions either by copying or moving
857 to a new track, we want to replace the old selection with the new ones
860 if (new_views.size() > 0) {
861 _editor->selection->set (new_views);
864 /* write commands for the accumulated diffs for all our modified playlists */
865 add_stateful_diff_commands_for_playlists (modified_playlists);
867 _editor->commit_reversible_command ();
871 RegionMoveDrag::finished_no_copy (
872 bool const changed_position,
873 bool const changed_tracks,
874 framecnt_t const drag_delta
877 RegionSelection new_views;
878 PlaylistSet modified_playlists;
879 PlaylistSet frozen_playlists;
882 /* all changes were made during motion event handlers */
883 _editor->commit_reversible_command ();
887 if (_x_constrained) {
888 _editor->begin_reversible_command (_("fixed time region drag"));
890 _editor->begin_reversible_command (_("region drag"));
893 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
895 RegionView* rv = i->view;
897 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
898 layer_t const dest_layer = i->layer;
900 if (rv->region()->locked()) {
907 if (changed_position && !_x_constrained) {
908 where = rv->region()->position() - drag_delta;
910 where = rv->region()->position();
913 if (changed_tracks) {
915 /* insert into new playlist */
917 RegionView* new_view = insert_region_into_playlist (
918 RegionFactory::create (rv->region ()), dest_rtv, dest_layer, where, modified_playlists
926 new_views.push_back (new_view);
928 /* remove from old playlist */
930 /* the region that used to be in the old playlist is not
931 moved to the new one - we use a copy of it. as a result,
932 any existing editor for the region should no longer be
935 rv->hide_region_editor();
936 rv->fake_set_opaque (false);
938 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
942 rv->region()->clear_changes ();
945 motion on the same track. plonk the previously reparented region
946 back to its original canvas group (its streamview).
947 No need to do anything for copies as they are fake regions which will be deleted.
950 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
951 rv->get_canvas_group()->property_y() = i->initial_y;
952 rv->get_time_axis_view().reveal_dependent_views (*rv);
954 /* just change the model */
956 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
958 if (dest_rtv->view()->layer_display() == Stacked) {
959 rv->region()->set_layer (dest_layer);
960 rv->region()->set_pending_explicit_relayer (true);
963 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
965 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
971 /* this movement may result in a crossfade being modified, so we need to get undo
972 data from the playlist as well as the region.
975 r = modified_playlists.insert (playlist);
977 playlist->clear_changes ();
980 rv->region()->set_position (where, (void*) this);
982 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
985 if (changed_tracks) {
987 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
988 was selected in all of them, then removing it from a playlist will have removed all
989 trace of it from _views (i.e. there were N regions selected, we removed 1,
990 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
991 corresponding regionview, and _views is now empty).
993 This could have invalidated any and all iterators into _views.
995 The heuristic we use here is: if the region selection is empty, break out of the loop
996 here. if the region selection is not empty, then restart the loop because we know that
997 we must have removed at least the region(view) we've just been working on as well as any
998 that we processed on previous iterations.
1000 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1001 we can just iterate.
1005 if (_views.empty()) {
1016 /* If we've created new regions either by copying or moving
1017 to a new track, we want to replace the old selection with the new ones
1020 if (new_views.size() > 0) {
1021 _editor->selection->set (new_views);
1024 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1028 /* write commands for the accumulated diffs for all our modified playlists */
1029 add_stateful_diff_commands_for_playlists (modified_playlists);
1031 _editor->commit_reversible_command ();
1034 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1035 * @param region Region to remove.
1036 * @param playlist playlist To remove from.
1037 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1038 * that clear_changes () is only called once per playlist.
1041 RegionMoveDrag::remove_region_from_playlist (
1042 boost::shared_ptr<Region> region,
1043 boost::shared_ptr<Playlist> playlist,
1044 PlaylistSet& modified_playlists
1047 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1050 playlist->clear_changes ();
1053 playlist->remove_region (region);
1057 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1058 * clearing the playlist's diff history first if necessary.
1059 * @param region Region to insert.
1060 * @param dest_rtv Destination RouteTimeAxisView.
1061 * @param dest_layer Destination layer.
1062 * @param where Destination position.
1063 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1064 * that clear_changes () is only called once per playlist.
1065 * @return New RegionView, or 0 if no insert was performed.
1068 RegionMoveDrag::insert_region_into_playlist (
1069 boost::shared_ptr<Region> region,
1070 RouteTimeAxisView* dest_rtv,
1073 PlaylistSet& modified_playlists
1076 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1077 if (!dest_playlist) {
1081 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1082 _new_region_view = 0;
1083 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1085 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1086 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1088 dest_playlist->clear_changes ();
1091 dest_playlist->add_region (region, where);
1093 if (dest_rtv->view()->layer_display() == Stacked) {
1094 region->set_layer (dest_layer);
1095 region->set_pending_explicit_relayer (true);
1100 assert (_new_region_view);
1102 return _new_region_view;
1106 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1108 _new_region_view = rv;
1112 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1114 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1115 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1117 _editor->session()->add_command (new StatefulDiffCommand (*i));
1126 RegionMoveDrag::aborted ()
1130 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1137 RegionMotionDrag::aborted ();
1142 RegionMotionDrag::aborted ()
1144 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1145 RegionView* rv = i->view;
1146 TimeAxisView* tv = &(rv->get_time_axis_view ());
1147 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1149 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1150 rv->get_canvas_group()->property_y() = 0;
1151 rv->get_time_axis_view().reveal_dependent_views (*rv);
1152 rv->fake_set_opaque (false);
1153 rv->move (-_total_x_delta, 0);
1154 rv->set_height (rtv->view()->child_height ());
1157 _editor->update_canvas_now ();
1160 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1161 : RegionMotionDrag (e, i, p, v, b),
1164 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1167 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1168 if (rtv && rtv->is_track()) {
1169 speed = rtv->track()->speed ();
1172 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1176 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1178 RegionMotionDrag::start_grab (event, c);
1180 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1183 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1184 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1186 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1188 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1189 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1191 _primary = v->view()->create_region_view (r, false, false);
1193 _primary->get_canvas_group()->show ();
1194 _primary->set_position (pos, 0);
1195 _views.push_back (DraggingView (_primary, this));
1197 _last_frame_position = pos;
1199 _item = _primary->get_canvas_group ();
1203 RegionInsertDrag::finished (GdkEvent *, bool)
1205 _editor->update_canvas_now ();
1207 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1209 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1210 _primary->get_canvas_group()->property_y() = 0;
1212 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1214 _editor->begin_reversible_command (_("insert region"));
1215 playlist->clear_changes ();
1216 playlist->add_region (_primary->region (), _last_frame_position);
1217 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1218 _editor->commit_reversible_command ();
1226 RegionInsertDrag::aborted ()
1233 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1234 : RegionMoveDrag (e, i, p, v, false, false)
1236 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1239 struct RegionSelectionByPosition {
1240 bool operator() (RegionView*a, RegionView* b) {
1241 return a->region()->position () < b->region()->position();
1246 RegionSpliceDrag::motion (GdkEvent* event, bool)
1248 /* Which trackview is this ? */
1250 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1251 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1252 layer_t layer = tvp.second;
1254 if (tv && tv->layer_display() == Overlaid) {
1258 /* The region motion is only processed if the pointer is over
1262 if (!tv || !tv->is_track()) {
1263 /* To make sure we hide the verbose canvas cursor when the mouse is
1264 not held over and audiotrack.
1266 _editor->hide_verbose_canvas_cursor ();
1272 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1278 RegionSelection copy (_editor->selection->regions);
1280 RegionSelectionByPosition cmp;
1283 framepos_t const pf = adjusted_current_frame (event);
1285 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1287 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1293 boost::shared_ptr<Playlist> playlist;
1295 if ((playlist = atv->playlist()) == 0) {
1299 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1304 if (pf < (*i)->region()->last_frame() + 1) {
1308 if (pf > (*i)->region()->first_frame()) {
1314 playlist->shuffle ((*i)->region(), dir);
1319 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1321 RegionMoveDrag::finished (event, movement_occurred);
1325 RegionSpliceDrag::aborted ()
1330 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1332 _view (dynamic_cast<MidiTimeAxisView*> (v))
1334 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1340 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1346 framepos_t const f = adjusted_current_frame (event);
1347 if (f < grab_frame()) {
1348 _region->set_position (f, this);
1351 /* again, don't use a zero-length region (see above) */
1352 framecnt_t const len = abs (f - grab_frame ());
1353 _region->set_length (len < 1 ? 1 : len, this);
1359 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1361 if (!movement_occurred) {
1366 _editor->commit_reversible_command ();
1371 RegionCreateDrag::add_region ()
1373 if (_editor->session()) {
1374 const TempoMap& map (_editor->session()->tempo_map());
1375 framecnt_t pos = grab_frame();
1376 const Meter& m = map.meter_at (pos);
1377 /* not that the frame rate used here can be affected by pull up/down which
1380 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1381 _region = _view->add_region (grab_frame(), len, false);
1386 RegionCreateDrag::aborted ()
1391 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1395 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1399 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1401 Gdk::Cursor* cursor;
1402 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1404 Drag::start_grab (event);
1406 region = &cnote->region_view();
1408 double const region_start = region->get_position_pixels();
1409 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1411 if (grab_x() <= middle_point) {
1412 cursor = _editor->left_side_trim_cursor;
1415 cursor = _editor->right_side_trim_cursor;
1419 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1421 if (event->motion.state & Keyboard::PrimaryModifier) {
1427 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1429 if (ms.size() > 1) {
1430 /* has to be relative, may make no sense otherwise */
1434 /* select this note; if it is already selected, preserve the existing selection,
1435 otherwise make this note the only one selected.
1437 region->note_selected (cnote, cnote->selected ());
1439 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1440 MidiRegionSelection::iterator next;
1443 (*r)->begin_resizing (at_front);
1449 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1451 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1452 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1453 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1458 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1460 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1461 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1462 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1467 NoteResizeDrag::aborted ()
1472 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1475 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1479 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1485 RegionGainDrag::finished (GdkEvent *, bool)
1491 RegionGainDrag::aborted ()
1496 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1497 : RegionDrag (e, i, p, v)
1498 , _have_transaction (false)
1500 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1504 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1507 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1508 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1510 if (tv && tv->is_track()) {
1511 speed = tv->track()->speed();
1514 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1515 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1516 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1518 framepos_t const pf = adjusted_current_frame (event);
1520 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1521 _operation = ContentsTrim;
1522 Drag::start_grab (event, _editor->trimmer_cursor);
1524 /* These will get overridden for a point trim.*/
1525 if (pf < (region_start + region_length/2)) {
1526 /* closer to start */
1527 _operation = StartTrim;
1528 Drag::start_grab (event, _editor->left_side_trim_cursor);
1531 _operation = EndTrim;
1532 Drag::start_grab (event, _editor->right_side_trim_cursor);
1536 switch (_operation) {
1538 _editor->show_verbose_time_cursor (region_start, 10);
1541 _editor->show_verbose_time_cursor (region_end, 10);
1544 _editor->show_verbose_time_cursor (pf, 10);
1550 TrimDrag::motion (GdkEvent* event, bool first_move)
1552 RegionView* rv = _primary;
1554 /* snap modifier works differently here..
1555 its current state has to be passed to the
1556 various trim functions in order to work properly
1560 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1561 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1562 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1564 if (tv && tv->is_track()) {
1565 speed = tv->track()->speed();
1568 framepos_t const pf = adjusted_current_frame (event);
1574 switch (_operation) {
1576 trim_type = "Region start trim";
1579 trim_type = "Region end trim";
1582 trim_type = "Region content trim";
1586 _editor->begin_reversible_command (trim_type);
1587 _have_transaction = true;
1589 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1590 RegionView* rv = i->view;
1591 rv->fake_set_opaque(false);
1592 rv->enable_display (false);
1593 rv->region()->clear_changes ();
1594 rv->region()->suspend_property_changes ();
1596 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1599 arv->temporarily_hide_envelope ();
1602 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1603 insert_result = _editor->motion_frozen_playlists.insert (pl);
1605 if (insert_result.second) {
1611 bool non_overlap_trim = false;
1613 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1614 non_overlap_trim = true;
1617 switch (_operation) {
1619 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1620 _editor->single_start_trim (*i->view, pf, non_overlap_trim);
1625 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1626 _editor->single_end_trim (*i->view, pf, non_overlap_trim);
1632 bool swap_direction = false;
1634 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1635 swap_direction = true;
1638 framecnt_t frame_delta = 0;
1640 bool left_direction = false;
1641 if (last_pointer_frame() > pf) {
1642 left_direction = true;
1645 if (left_direction) {
1646 frame_delta = (last_pointer_frame() - pf);
1648 frame_delta = (pf - last_pointer_frame());
1651 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1652 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1658 switch (_operation) {
1660 _editor->show_verbose_time_cursor((framepos_t) (rv->region()->position()/speed), 10);
1663 _editor->show_verbose_time_cursor((framepos_t) (rv->region()->last_frame()/speed), 10);
1666 _editor->show_verbose_time_cursor (pf, 10);
1673 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1675 if (movement_occurred) {
1676 motion (event, false);
1678 if (!_editor->selection->selected (_primary)) {
1679 _editor->thaw_region_after_trim (*_primary);
1682 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1683 _editor->thaw_region_after_trim (*i->view);
1684 i->view->enable_display (true);
1685 i->view->fake_set_opaque (true);
1686 if (_have_transaction) {
1687 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1691 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1695 _editor->motion_frozen_playlists.clear ();
1697 if (_have_transaction) {
1698 _editor->commit_reversible_command();
1702 /* no mouse movement */
1703 _editor->point_trim (event, adjusted_current_frame (event));
1708 TrimDrag::aborted ()
1710 /* Our motion method is changing model state, so use the Undo system
1711 to cancel. Perhaps not ideal, as this will leave an Undo point
1712 behind which may be slightly odd from the user's point of view.
1717 if (_have_transaction) {
1722 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1726 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1728 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1733 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1736 // create a dummy marker for visual representation of moving the copy.
1737 // The actual copying is not done before we reach the finish callback.
1739 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1740 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1741 *new MeterSection (_marker->meter()));
1743 _item = &new_marker->the_item ();
1744 _marker = new_marker;
1748 MetricSection& section (_marker->meter());
1750 if (!section.movable()) {
1756 Drag::start_grab (event, cursor);
1758 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1760 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1764 MeterMarkerDrag::motion (GdkEvent* event, bool)
1766 framepos_t const pf = adjusted_current_frame (event);
1768 _marker->set_position (pf);
1770 _editor->show_verbose_time_cursor (pf, 10);
1774 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1776 if (!movement_occurred) {
1780 motion (event, false);
1784 TempoMap& map (_editor->session()->tempo_map());
1785 map.bbt_time (last_pointer_frame(), when);
1787 if (_copy == true) {
1788 _editor->begin_reversible_command (_("copy meter mark"));
1789 XMLNode &before = map.get_state();
1790 map.add_meter (_marker->meter(), when);
1791 XMLNode &after = map.get_state();
1792 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1793 _editor->commit_reversible_command ();
1795 // delete the dummy marker we used for visual representation of copying.
1796 // a new visual marker will show up automatically.
1799 _editor->begin_reversible_command (_("move meter mark"));
1800 XMLNode &before = map.get_state();
1801 map.move_meter (_marker->meter(), when);
1802 XMLNode &after = map.get_state();
1803 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1804 _editor->commit_reversible_command ();
1809 MeterMarkerDrag::aborted ()
1811 _marker->set_position (_marker->meter().frame ());
1814 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1818 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1820 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1825 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1830 // create a dummy marker for visual representation of moving the copy.
1831 // The actual copying is not done before we reach the finish callback.
1833 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1834 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
1835 *new TempoSection (_marker->tempo()));
1837 _item = &new_marker->the_item ();
1838 _marker = new_marker;
1842 MetricSection& section (_marker->tempo());
1844 if (!section.movable()) {
1849 Drag::start_grab (event, cursor);
1851 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1852 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1856 TempoMarkerDrag::motion (GdkEvent* event, bool)
1858 framepos_t const pf = adjusted_current_frame (event);
1859 _marker->set_position (pf);
1860 _editor->show_verbose_time_cursor (pf, 10);
1864 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1866 if (!movement_occurred) {
1870 motion (event, false);
1874 TempoMap& map (_editor->session()->tempo_map());
1875 map.bbt_time (last_pointer_frame(), when);
1877 if (_copy == true) {
1878 _editor->begin_reversible_command (_("copy tempo mark"));
1879 XMLNode &before = map.get_state();
1880 map.add_tempo (_marker->tempo(), when);
1881 XMLNode &after = map.get_state();
1882 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1883 _editor->commit_reversible_command ();
1885 // delete the dummy marker we used for visual representation of copying.
1886 // a new visual marker will show up automatically.
1889 _editor->begin_reversible_command (_("move tempo mark"));
1890 XMLNode &before = map.get_state();
1891 map.move_tempo (_marker->tempo(), when);
1892 XMLNode &after = map.get_state();
1893 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1894 _editor->commit_reversible_command ();
1899 TempoMarkerDrag::aborted ()
1901 _marker->set_position (_marker->tempo().frame());
1904 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1908 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
1910 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1915 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1917 Drag::start_grab (event, c);
1921 framepos_t where = _editor->event_frame (event, 0, 0);
1923 _editor->snap_to_with_modifier (where, event);
1924 _editor->playhead_cursor->set_position (where);
1928 if (_cursor == _editor->playhead_cursor) {
1929 _editor->_dragging_playhead = true;
1931 Session* s = _editor->session ();
1934 if (_was_rolling && _stop) {
1938 if (s->is_auditioning()) {
1939 s->cancel_audition ();
1942 s->request_suspend_timecode_transmission ();
1944 if (s->timecode_transmission_suspended ()) {
1945 framepos_t const f = _editor->playhead_cursor->current_frame;
1946 s->send_mmc_locate (f);
1947 s->send_full_time_code (f);
1952 _pointer_frame_offset = raw_grab_frame() - _cursor->current_frame;
1954 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1958 CursorDrag::motion (GdkEvent* event, bool)
1960 framepos_t const adjusted_frame = adjusted_current_frame (event);
1962 if (adjusted_frame == last_pointer_frame()) {
1966 _cursor->set_position (adjusted_frame);
1968 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1970 Session* s = _editor->session ();
1971 if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
1972 framepos_t const f = _editor->playhead_cursor->current_frame;
1973 s->send_mmc_locate (f);
1974 s->send_full_time_code (f);
1979 _editor->update_canvas_now ();
1981 _editor->UpdateAllTransportClocks (_cursor->current_frame);
1985 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
1987 _editor->_dragging_playhead = false;
1989 if (!movement_occurred && _stop) {
1993 motion (event, false);
1995 if (_item == &_editor->playhead_cursor->canvas_item) {
1996 Session* s = _editor->session ();
1998 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
1999 _editor->_pending_locate_request = true;
2000 s->request_resume_timecode_transmission ();
2006 CursorDrag::aborted ()
2008 if (_editor->_dragging_playhead) {
2009 _editor->session()->request_resume_timecode_transmission ();
2010 _editor->_dragging_playhead = false;
2013 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2016 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2017 : RegionDrag (e, i, p, v)
2019 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2023 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2025 Drag::start_grab (event, cursor);
2027 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2028 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2030 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2031 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2033 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2037 FadeInDrag::motion (GdkEvent* event, bool)
2039 framecnt_t fade_length;
2041 framepos_t const pos = adjusted_current_frame (event);
2043 boost::shared_ptr<Region> region = _primary->region ();
2045 if (pos < (region->position() + 64)) {
2046 fade_length = 64; // this should be a minimum defined somewhere
2047 } else if (pos > region->last_frame()) {
2048 fade_length = region->length();
2050 fade_length = pos - region->position();
2053 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2055 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2061 tmp->reset_fade_in_shape_width (fade_length);
2062 tmp->show_fade_line((framecnt_t) fade_length);
2065 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2069 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2071 if (!movement_occurred) {
2075 framecnt_t fade_length;
2077 framepos_t const pos = adjusted_current_frame (event);
2079 boost::shared_ptr<Region> region = _primary->region ();
2081 if (pos < (region->position() + 64)) {
2082 fade_length = 64; // this should be a minimum defined somewhere
2083 } else if (pos > region->last_frame()) {
2084 fade_length = region->length();
2086 fade_length = pos - region->position();
2089 _editor->begin_reversible_command (_("change fade in length"));
2091 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2093 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2099 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2100 XMLNode &before = alist->get_state();
2102 tmp->audio_region()->set_fade_in_length (fade_length);
2103 tmp->audio_region()->set_fade_in_active (true);
2104 tmp->hide_fade_line();
2106 XMLNode &after = alist->get_state();
2107 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2110 _editor->commit_reversible_command ();
2114 FadeInDrag::aborted ()
2116 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2117 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2123 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2124 tmp->hide_fade_line();
2128 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2129 : RegionDrag (e, i, p, v)
2131 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2135 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2137 Drag::start_grab (event, cursor);
2139 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2140 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2142 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2143 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2145 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2149 FadeOutDrag::motion (GdkEvent* event, bool)
2151 framecnt_t fade_length;
2153 framepos_t const pos = adjusted_current_frame (event);
2155 boost::shared_ptr<Region> region = _primary->region ();
2157 if (pos > (region->last_frame() - 64)) {
2158 fade_length = 64; // this should really be a minimum fade defined somewhere
2160 else if (pos < region->position()) {
2161 fade_length = region->length();
2164 fade_length = region->last_frame() - pos;
2167 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2169 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2175 tmp->reset_fade_out_shape_width (fade_length);
2176 tmp->show_fade_line(region->length() - fade_length);
2179 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2183 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2185 if (!movement_occurred) {
2189 framecnt_t fade_length;
2191 framepos_t const pos = adjusted_current_frame (event);
2193 boost::shared_ptr<Region> region = _primary->region ();
2195 if (pos > (region->last_frame() - 64)) {
2196 fade_length = 64; // this should really be a minimum fade defined somewhere
2198 else if (pos < region->position()) {
2199 fade_length = region->length();
2202 fade_length = region->last_frame() - pos;
2205 _editor->begin_reversible_command (_("change fade out length"));
2207 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2209 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2215 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2216 XMLNode &before = alist->get_state();
2218 tmp->audio_region()->set_fade_out_length (fade_length);
2219 tmp->audio_region()->set_fade_out_active (true);
2220 tmp->hide_fade_line();
2222 XMLNode &after = alist->get_state();
2223 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2226 _editor->commit_reversible_command ();
2230 FadeOutDrag::aborted ()
2232 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2233 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2239 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2240 tmp->hide_fade_line();
2244 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2247 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2249 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2252 _points.push_back (Gnome::Art::Point (0, 0));
2253 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2255 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2256 _line->property_width_pixels() = 1;
2257 _line->property_points () = _points;
2260 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2263 MarkerDrag::~MarkerDrag ()
2265 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2271 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2273 Drag::start_grab (event, cursor);
2277 Location *location = _editor->find_location_from_marker (_marker, is_start);
2278 _editor->_dragging_edit_point = true;
2280 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2282 update_item (location);
2284 // _drag_line->show();
2285 // _line->raise_to_top();
2288 _editor->show_verbose_time_cursor (location->start(), 10);
2290 _editor->show_verbose_time_cursor (location->end(), 10);
2293 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2296 case Selection::Toggle:
2297 _editor->selection->toggle (_marker);
2299 case Selection::Set:
2300 if (!_editor->selection->selected (_marker)) {
2301 _editor->selection->set (_marker);
2304 case Selection::Extend:
2306 Locations::LocationList ll;
2307 list<Marker*> to_add;
2309 _editor->selection->markers.range (s, e);
2310 s = min (_marker->position(), s);
2311 e = max (_marker->position(), e);
2314 if (e < max_framepos) {
2317 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2318 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2319 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2322 to_add.push_back (lm->start);
2325 to_add.push_back (lm->end);
2329 if (!to_add.empty()) {
2330 _editor->selection->add (to_add);
2334 case Selection::Add:
2335 _editor->selection->add (_marker);
2339 /* Set up copies for us to manipulate during the drag */
2341 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2342 Location* l = _editor->find_location_from_marker (*i, is_start);
2343 _copied_locations.push_back (new Location (*l));
2348 MarkerDrag::motion (GdkEvent* event, bool)
2350 framecnt_t f_delta = 0;
2352 bool move_both = false;
2354 Location *real_location;
2355 Location *copy_location = 0;
2357 framepos_t const newframe = adjusted_current_frame (event);
2359 framepos_t next = newframe;
2361 if (newframe == last_pointer_frame()) {
2365 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2369 MarkerSelection::iterator i;
2370 list<Location*>::iterator x;
2372 /* find the marker we're dragging, and compute the delta */
2374 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2375 x != _copied_locations.end() && i != _editor->selection->markers.end();
2381 if (marker == _marker) {
2383 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2388 if (real_location->is_mark()) {
2389 f_delta = newframe - copy_location->start();
2393 switch (marker->type()) {
2395 case Marker::LoopStart:
2396 case Marker::PunchIn:
2397 f_delta = newframe - copy_location->start();
2401 case Marker::LoopEnd:
2402 case Marker::PunchOut:
2403 f_delta = newframe - copy_location->end();
2406 /* what kind of marker is this ? */
2414 if (i == _editor->selection->markers.end()) {
2415 /* hmm, impossible - we didn't find the dragged marker */
2419 /* now move them all */
2421 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2422 x != _copied_locations.end() && i != _editor->selection->markers.end();
2428 /* call this to find out if its the start or end */
2430 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2434 if (real_location->locked()) {
2438 if (copy_location->is_mark()) {
2442 copy_location->set_start (copy_location->start() + f_delta);
2446 framepos_t new_start = copy_location->start() + f_delta;
2447 framepos_t new_end = copy_location->end() + f_delta;
2449 if (is_start) { // start-of-range marker
2452 copy_location->set_start (new_start);
2453 copy_location->set_end (new_end);
2454 } else if (new_start < copy_location->end()) {
2455 copy_location->set_start (new_start);
2457 _editor->snap_to (next, 1, true);
2458 copy_location->set_end (next);
2459 copy_location->set_start (newframe);
2462 } else { // end marker
2465 copy_location->set_end (new_end);
2466 copy_location->set_start (new_start);
2467 } else if (new_end > copy_location->start()) {
2468 copy_location->set_end (new_end);
2469 } else if (newframe > 0) {
2470 _editor->snap_to (next, -1, true);
2471 copy_location->set_start (next);
2472 copy_location->set_end (newframe);
2477 update_item (copy_location);
2479 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2482 lm->set_position (copy_location->start(), copy_location->end());
2486 assert (!_copied_locations.empty());
2488 _editor->show_verbose_time_cursor (newframe, 10);
2491 _editor->update_canvas_now ();
2496 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2498 if (!movement_occurred) {
2500 /* just a click, do nothing but finish
2501 off the selection process
2504 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2507 case Selection::Set:
2508 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2509 _editor->selection->set (_marker);
2513 case Selection::Toggle:
2514 case Selection::Extend:
2515 case Selection::Add:
2522 _editor->_dragging_edit_point = false;
2524 _editor->begin_reversible_command ( _("move marker") );
2525 XMLNode &before = _editor->session()->locations()->get_state();
2527 MarkerSelection::iterator i;
2528 list<Location*>::iterator x;
2531 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2532 x != _copied_locations.end() && i != _editor->selection->markers.end();
2535 Location * location = _editor->find_location_from_marker (*i, is_start);
2539 if (location->locked()) {
2543 if (location->is_mark()) {
2544 location->set_start ((*x)->start());
2546 location->set ((*x)->start(), (*x)->end());
2551 XMLNode &after = _editor->session()->locations()->get_state();
2552 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2553 _editor->commit_reversible_command ();
2559 MarkerDrag::aborted ()
2565 MarkerDrag::update_item (Location* location)
2567 double const x1 = _editor->frame_to_pixel (location->start());
2569 _points.front().set_x(x1);
2570 _points.back().set_x(x1);
2571 _line->property_points() = _points;
2574 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2576 _cumulative_x_drag (0),
2577 _cumulative_y_drag (0)
2579 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2581 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2587 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2589 Drag::start_grab (event, _editor->fader_cursor);
2591 // start the grab at the center of the control point so
2592 // the point doesn't 'jump' to the mouse after the first drag
2593 _fixed_grab_x = _point->get_x();
2594 _fixed_grab_y = _point->get_y();
2596 float const fraction = 1 - (_point->get_y() / _point->line().height());
2598 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2600 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2601 event->button.x + 10, event->button.y + 10);
2603 _editor->show_verbose_canvas_cursor ();
2607 ControlPointDrag::motion (GdkEvent* event, bool)
2609 double dx = _drags->current_pointer_x() - last_pointer_x();
2610 double dy = _drags->current_pointer_y() - last_pointer_y();
2612 if (event->button.state & Keyboard::SecondaryModifier) {
2617 /* coordinate in pixels relative to the start of the region (for region-based automation)
2618 or track (for track-based automation) */
2619 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2620 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2622 // calculate zero crossing point. back off by .01 to stay on the
2623 // positive side of zero
2624 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2626 // make sure we hit zero when passing through
2627 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2631 if (_x_constrained) {
2634 if (_y_constrained) {
2638 _cumulative_x_drag = cx - _fixed_grab_x;
2639 _cumulative_y_drag = cy - _fixed_grab_y;
2643 cy = min ((double) _point->line().height(), cy);
2645 framepos_t cx_frames = _editor->unit_to_frame (cx);
2647 if (!_x_constrained) {
2648 _editor->snap_to_with_modifier (cx_frames, event);
2651 cx_frames = min (cx_frames, _point->line().maximum_time());
2653 float const fraction = 1.0 - (cy / _point->line().height());
2655 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2657 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2659 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2663 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2665 if (!movement_occurred) {
2669 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2670 _editor->reset_point_selection ();
2674 motion (event, false);
2677 _point->line().end_drag ();
2678 _editor->session()->commit_reversible_command ();
2682 ControlPointDrag::aborted ()
2684 _point->line().reset ();
2688 ControlPointDrag::active (Editing::MouseMode m)
2690 if (m == Editing::MouseGain) {
2691 /* always active in mouse gain */
2695 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2696 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2699 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2702 _cumulative_y_drag (0)
2704 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2708 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2710 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2713 _item = &_line->grab_item ();
2715 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2716 origin, and ditto for y.
2719 double cx = event->button.x;
2720 double cy = event->button.y;
2722 _line->parent_group().w2i (cx, cy);
2724 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2729 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2730 /* no adjacent points */
2734 Drag::start_grab (event, _editor->fader_cursor);
2736 /* store grab start in parent frame */
2741 double fraction = 1.0 - (cy / _line->height());
2743 _line->start_drag_line (before, after, fraction);
2745 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2746 event->button.x + 10, event->button.y + 10);
2748 _editor->show_verbose_canvas_cursor ();
2752 LineDrag::motion (GdkEvent* event, bool)
2754 double dy = _drags->current_pointer_y() - last_pointer_y();
2756 if (event->button.state & Keyboard::SecondaryModifier) {
2760 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2762 _cumulative_y_drag = cy - _fixed_grab_y;
2765 cy = min ((double) _line->height(), cy);
2767 double const fraction = 1.0 - (cy / _line->height());
2771 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2777 /* we are ignoring x position for this drag, so we can just pass in anything */
2778 _line->drag_motion (0, fraction, true, push);
2780 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2784 LineDrag::finished (GdkEvent* event, bool)
2786 motion (event, false);
2788 _editor->session()->commit_reversible_command ();
2792 LineDrag::aborted ()
2797 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2800 _cumulative_x_drag (0)
2802 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2806 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2808 Drag::start_grab (event);
2810 _line = reinterpret_cast<SimpleLine*> (_item);
2813 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2815 double cx = event->button.x;
2816 double cy = event->button.y;
2818 _item->property_parent().get_value()->w2i(cx, cy);
2820 /* store grab start in parent frame */
2821 _region_view_grab_x = cx;
2823 _before = _line->property_x1();
2825 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2827 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2831 FeatureLineDrag::motion (GdkEvent* event, bool)
2833 double dx = _drags->current_pointer_x() - last_pointer_x();
2835 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2837 _cumulative_x_drag += dx;
2839 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2848 _line->property_x1() = cx;
2849 _line->property_x2() = cx;
2851 _before = _line->property_x1();
2855 FeatureLineDrag::finished (GdkEvent* event, bool)
2857 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2858 _arv->update_transient(_before, _line->property_x1());
2862 FeatureLineDrag::aborted ()
2867 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2870 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2874 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2876 Drag::start_grab (event);
2877 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2881 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2888 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2890 framepos_t grab = grab_frame ();
2891 if (Config->get_rubberbanding_snaps_to_grid ()) {
2892 _editor->snap_to_with_modifier (grab, event);
2895 /* base start and end on initial click position */
2905 if (_drags->current_pointer_y() < grab_y()) {
2906 y1 = _drags->current_pointer_y();
2909 y2 = _drags->current_pointer_y();
2914 if (start != end || y1 != y2) {
2916 double x1 = _editor->frame_to_pixel (start);
2917 double x2 = _editor->frame_to_pixel (end);
2919 _editor->rubberband_rect->property_x1() = x1;
2920 _editor->rubberband_rect->property_y1() = y1;
2921 _editor->rubberband_rect->property_x2() = x2;
2922 _editor->rubberband_rect->property_y2() = y2;
2924 _editor->rubberband_rect->show();
2925 _editor->rubberband_rect->raise_to_top();
2927 _editor->show_verbose_time_cursor (pf, 10);
2932 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2934 if (movement_occurred) {
2936 motion (event, false);
2939 if (_drags->current_pointer_y() < grab_y()) {
2940 y1 = _drags->current_pointer_y();
2943 y2 = _drags->current_pointer_y();
2948 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2951 _editor->begin_reversible_command (_("rubberband selection"));
2953 if (grab_frame() < last_pointer_frame()) {
2954 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
2956 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
2960 _editor->commit_reversible_command ();
2964 if (!getenv("ARDOUR_SAE")) {
2965 _editor->selection->clear_tracks();
2967 _editor->selection->clear_regions();
2968 _editor->selection->clear_points ();
2969 _editor->selection->clear_lines ();
2972 _editor->rubberband_rect->hide();
2976 RubberbandSelectDrag::aborted ()
2978 _editor->rubberband_rect->hide ();
2981 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
2982 : RegionDrag (e, i, p, v)
2984 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
2988 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2990 Drag::start_grab (event, cursor);
2992 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2996 TimeFXDrag::motion (GdkEvent* event, bool)
2998 RegionView* rv = _primary;
3000 framepos_t const pf = adjusted_current_frame (event);
3002 if (pf > rv->region()->position()) {
3003 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3006 _editor->show_verbose_time_cursor (pf, 10);
3010 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3012 _primary->get_time_axis_view().hide_timestretch ();
3014 if (!movement_occurred) {
3018 if (last_pointer_frame() < _primary->region()->position()) {
3019 /* backwards drag of the left edge - not usable */
3023 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3025 float percentage = (double) newlen / (double) _primary->region()->length();
3027 #ifndef USE_RUBBERBAND
3028 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3029 if (_primary->region()->data_type() == DataType::AUDIO) {
3030 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3034 _editor->begin_reversible_command (_("timestretch"));
3036 // XXX how do timeFX on multiple regions ?
3041 if (_editor->time_stretch (rs, percentage) == -1) {
3042 error << _("An error occurred while executing time stretch operation") << endmsg;
3047 TimeFXDrag::aborted ()
3049 _primary->get_time_axis_view().hide_timestretch ();
3052 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3055 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3059 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3061 Drag::start_grab (event);
3065 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3067 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3071 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3073 if (movement_occurred && _editor->session()) {
3074 /* make sure we stop */
3075 _editor->session()->request_transport_speed (0.0);
3080 ScrubDrag::aborted ()
3085 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3089 , _original_pointer_time_axis (-1)
3090 , _last_pointer_time_axis (-1)
3092 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3096 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3098 framepos_t start = 0;
3101 if (_editor->session() == 0) {
3105 Gdk::Cursor* cursor = 0;
3107 switch (_operation) {
3108 case CreateSelection:
3109 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3114 cursor = _editor->selector_cursor;
3115 Drag::start_grab (event, cursor);
3118 case SelectionStartTrim:
3119 if (_editor->clicked_axisview) {
3120 _editor->clicked_axisview->order_selection_trims (_item, true);
3122 Drag::start_grab (event, _editor->left_side_trim_cursor);
3123 start = _editor->selection->time[_editor->clicked_selection].start;
3124 _pointer_frame_offset = raw_grab_frame() - start;
3127 case SelectionEndTrim:
3128 if (_editor->clicked_axisview) {
3129 _editor->clicked_axisview->order_selection_trims (_item, false);
3131 Drag::start_grab (event, _editor->right_side_trim_cursor);
3132 end = _editor->selection->time[_editor->clicked_selection].end;
3133 _pointer_frame_offset = raw_grab_frame() - end;
3137 start = _editor->selection->time[_editor->clicked_selection].start;
3138 Drag::start_grab (event, cursor);
3139 _pointer_frame_offset = raw_grab_frame() - start;
3143 if (_operation == SelectionMove) {
3144 _editor->show_verbose_time_cursor (start, 10);
3146 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3149 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3153 SelectionDrag::motion (GdkEvent* event, bool first_move)
3155 framepos_t start = 0;
3159 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3160 if (pending_time_axis.first == 0) {
3164 framepos_t const pending_position = adjusted_current_frame (event);
3166 /* only alter selection if things have changed */
3168 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3172 switch (_operation) {
3173 case CreateSelection:
3175 framepos_t grab = grab_frame ();
3178 _editor->snap_to (grab);
3181 if (pending_position < grab_frame()) {
3182 start = pending_position;
3185 end = pending_position;
3189 /* first drag: Either add to the selection
3190 or create a new selection
3196 /* adding to the selection */
3197 _editor->set_selected_track_as_side_effect (Selection::Add);
3198 //_editor->selection->add (_editor->clicked_axisview);
3199 _editor->clicked_selection = _editor->selection->add (start, end);
3204 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3205 //_editor->selection->set (_editor->clicked_axisview);
3206 _editor->set_selected_track_as_side_effect (Selection::Set);
3209 _editor->clicked_selection = _editor->selection->set (start, end);
3213 /* select the track that we're in */
3214 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3215 // _editor->set_selected_track_as_side_effect (Selection::Add);
3216 _editor->selection->add (pending_time_axis.first);
3217 _added_time_axes.push_back (pending_time_axis.first);
3220 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3221 tracks that we selected in the first place.
3224 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3225 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3227 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3228 while (i != _added_time_axes.end()) {
3230 list<TimeAxisView*>::iterator tmp = i;
3233 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3234 _editor->selection->remove (*i);
3235 _added_time_axes.remove (*i);
3244 case SelectionStartTrim:
3246 start = _editor->selection->time[_editor->clicked_selection].start;
3247 end = _editor->selection->time[_editor->clicked_selection].end;
3249 if (pending_position > end) {
3252 start = pending_position;
3256 case SelectionEndTrim:
3258 start = _editor->selection->time[_editor->clicked_selection].start;
3259 end = _editor->selection->time[_editor->clicked_selection].end;
3261 if (pending_position < start) {
3264 end = pending_position;
3271 start = _editor->selection->time[_editor->clicked_selection].start;
3272 end = _editor->selection->time[_editor->clicked_selection].end;
3274 length = end - start;
3276 start = pending_position;
3277 _editor->snap_to (start);
3279 end = start + length;
3284 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3285 _editor->start_canvas_autoscroll (1, 0);
3289 _editor->selection->replace (_editor->clicked_selection, start, end);
3292 if (_operation == SelectionMove) {
3293 _editor->show_verbose_time_cursor(start, 10);
3295 _editor->show_verbose_time_cursor(pending_position, 10);
3300 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3302 Session* s = _editor->session();
3304 if (movement_occurred) {
3305 motion (event, false);
3306 /* XXX this is not object-oriented programming at all. ick */
3307 if (_editor->selection->time.consolidate()) {
3308 _editor->selection->TimeChanged ();
3311 /* XXX what if its a music time selection? */
3312 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3313 s->request_play_range (&_editor->selection->time, true);
3318 /* just a click, no pointer movement.*/
3320 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3321 _editor->selection->clear_time();
3324 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3325 _editor->selection->set (_editor->clicked_axisview);
3328 if (s && s->get_play_range () && s->transport_rolling()) {
3329 s->request_stop (false, false);
3334 _editor->stop_canvas_autoscroll ();
3338 SelectionDrag::aborted ()
3343 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3348 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3350 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3351 physical_screen_height (_editor->get_window()));
3352 _drag_rect->hide ();
3354 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3355 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3359 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3361 if (_editor->session() == 0) {
3365 Gdk::Cursor* cursor = 0;
3367 if (!_editor->temp_location) {
3368 _editor->temp_location = new Location (*_editor->session());
3371 switch (_operation) {
3372 case CreateRangeMarker:
3373 case CreateTransportMarker:
3374 case CreateCDMarker:
3376 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3381 cursor = _editor->selector_cursor;
3385 Drag::start_grab (event, cursor);
3387 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3391 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3393 framepos_t start = 0;
3395 ArdourCanvas::SimpleRect *crect;
3397 switch (_operation) {
3398 case CreateRangeMarker:
3399 crect = _editor->range_bar_drag_rect;
3401 case CreateTransportMarker:
3402 crect = _editor->transport_bar_drag_rect;
3404 case CreateCDMarker:
3405 crect = _editor->cd_marker_bar_drag_rect;
3408 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3413 framepos_t const pf = adjusted_current_frame (event);
3415 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3416 framepos_t grab = grab_frame ();
3417 _editor->snap_to (grab);
3419 if (pf < grab_frame()) {
3427 /* first drag: Either add to the selection
3428 or create a new selection.
3433 _editor->temp_location->set (start, end);
3437 update_item (_editor->temp_location);
3439 //_drag_rect->raise_to_top();
3444 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3445 _editor->start_canvas_autoscroll (1, 0);
3449 _editor->temp_location->set (start, end);
3451 double x1 = _editor->frame_to_pixel (start);
3452 double x2 = _editor->frame_to_pixel (end);
3453 crect->property_x1() = x1;
3454 crect->property_x2() = x2;
3456 update_item (_editor->temp_location);
3459 _editor->show_verbose_time_cursor (pf, 10);
3464 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3466 Location * newloc = 0;
3470 if (movement_occurred) {
3471 motion (event, false);
3474 switch (_operation) {
3475 case CreateRangeMarker:
3476 case CreateCDMarker:
3478 _editor->begin_reversible_command (_("new range marker"));
3479 XMLNode &before = _editor->session()->locations()->get_state();
3480 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3481 if (_operation == CreateCDMarker) {
3482 flags = Location::IsRangeMarker | Location::IsCDMarker;
3483 _editor->cd_marker_bar_drag_rect->hide();
3486 flags = Location::IsRangeMarker;
3487 _editor->range_bar_drag_rect->hide();
3489 newloc = new Location (
3490 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3493 _editor->session()->locations()->add (newloc, true);
3494 XMLNode &after = _editor->session()->locations()->get_state();
3495 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3496 _editor->commit_reversible_command ();
3500 case CreateTransportMarker:
3501 // popup menu to pick loop or punch
3502 _editor->new_transport_marker_context_menu (&event->button, _item);
3506 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3508 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3513 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3515 if (end == max_framepos) {
3516 end = _editor->session()->current_end_frame ();
3519 if (start == max_framepos) {
3520 start = _editor->session()->current_start_frame ();
3523 switch (_editor->mouse_mode) {
3525 /* find the two markers on either side and then make the selection from it */
3526 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3530 /* find the two markers on either side of the click and make the range out of it */
3531 _editor->selection->set (start, end);
3540 _editor->stop_canvas_autoscroll ();
3544 RangeMarkerBarDrag::aborted ()
3550 RangeMarkerBarDrag::update_item (Location* location)
3552 double const x1 = _editor->frame_to_pixel (location->start());
3553 double const x2 = _editor->frame_to_pixel (location->end());
3555 _drag_rect->property_x1() = x1;
3556 _drag_rect->property_x2() = x2;
3559 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3562 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3566 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3568 Drag::start_grab (event, _editor->zoom_cursor);
3569 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3573 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3578 framepos_t const pf = adjusted_current_frame (event);
3580 framepos_t grab = grab_frame ();
3581 _editor->snap_to_with_modifier (grab, event);
3583 /* base start and end on initial click position */
3595 _editor->zoom_rect->show();
3596 _editor->zoom_rect->raise_to_top();
3599 _editor->reposition_zoom_rect(start, end);
3601 _editor->show_verbose_time_cursor (pf, 10);
3606 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3608 if (movement_occurred) {
3609 motion (event, false);
3611 if (grab_frame() < last_pointer_frame()) {
3612 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3614 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3617 _editor->temporal_zoom_to_frame (false, grab_frame());
3619 temporal_zoom_step (false);
3620 center_screen (grab_frame());
3624 _editor->zoom_rect->hide();
3628 MouseZoomDrag::aborted ()
3630 _editor->zoom_rect->hide ();
3633 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3635 , _cumulative_dx (0)
3636 , _cumulative_dy (0)
3638 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3640 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3641 _region = &_primary->region_view ();
3642 _note_height = _region->midi_stream_view()->note_height ();
3646 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3648 Drag::start_grab (event);
3650 if (!(_was_selected = _primary->selected())) {
3652 /* tertiary-click means extend selection - we'll do that on button release,
3653 so don't add it here, because otherwise we make it hard to figure
3654 out the "extend-to" range.
3657 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3660 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3663 _region->note_selected (_primary, true);
3665 _region->unique_select (_primary);
3671 /** @return Current total drag x change in frames */
3673 NoteDrag::total_dx () const
3676 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3678 /* primary note time */
3679 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3681 /* new time of the primary note relative to the region position */
3682 frameoffset_t const st = n + dx;
3684 /* snap and return corresponding delta */
3685 return _region->snap_frame_to_frame (st) - n;
3688 /** @return Current total drag y change in notes */
3690 NoteDrag::total_dy () const
3692 /* this is `backwards' to make increasing note number go in the right direction */
3693 double const dy = _drags->current_pointer_y() - grab_y();
3698 if (abs (dy) >= _note_height) {
3700 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3702 ndy = (int8_t) floor (dy / _note_height / 2.0);
3706 /* more positive value = higher pitch and higher y-axis position on track,
3707 which is the inverse of the X-centric geometric universe
3714 NoteDrag::motion (GdkEvent *, bool)
3716 /* Total change in x and y since the start of the drag */
3717 frameoffset_t const dx = total_dx ();
3718 int8_t const dy = -total_dy ();
3720 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3721 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3722 double const tdy = dy * _note_height - _cumulative_dy;
3725 _cumulative_dx += tdx;
3726 _cumulative_dy += tdy;
3728 int8_t note_delta = total_dy();
3730 _region->move_selection (tdx, tdy, note_delta);
3733 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3734 (int) floor (_primary->note()->note() + note_delta));
3736 _editor->show_verbose_canvas_cursor_with (buf);
3741 NoteDrag::finished (GdkEvent* ev, bool moved)
3744 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3746 if (_was_selected) {
3747 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3749 _region->note_deselected (_primary);
3752 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3753 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3755 if (!extend && !add && _region->selection_size() > 1) {
3756 _region->unique_select (_primary);
3757 } else if (extend) {
3758 _region->note_selected (_primary, true, true);
3760 /* it was added during button press */
3765 _region->note_dropped (_primary, total_dx(), - total_dy());
3770 NoteDrag::aborted ()
3775 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3776 : Drag (editor, item)
3778 , _nothing_to_drag (false)
3780 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3782 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3785 /* get all lines in the automation view */
3786 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3788 /* find those that overlap the ranges being dragged */
3789 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3790 while (i != lines.end ()) {
3791 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3794 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3796 /* check this range against all the AudioRanges that we are using */
3797 list<AudioRange>::const_iterator k = _ranges.begin ();
3798 while (k != _ranges.end()) {
3799 if (k->coverage (r.first, r.second) != OverlapNone) {
3805 /* add it to our list if it overlaps at all */
3806 if (k != _ranges.end()) {
3811 _lines.push_back (n);
3817 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3821 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3823 Drag::start_grab (event, cursor);
3825 /* Get line states before we start changing things */
3826 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3827 i->state = &i->line->get_state ();
3830 if (_ranges.empty()) {
3832 /* No selected time ranges: drag all points */
3833 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3834 uint32_t const N = i->line->npoints ();
3835 for (uint32_t j = 0; j < N; ++j) {
3836 i->points.push_back (i->line->nth (j));
3842 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3844 framecnt_t const half = (i->start + i->end) / 2;
3846 /* find the line that this audio range starts in */
3847 list<Line>::iterator j = _lines.begin();
3848 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3852 if (j != _lines.end()) {
3853 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3855 /* j is the line that this audio range starts in; fade into it;
3856 64 samples length plucked out of thin air.
3859 framepos_t a = i->start + 64;
3864 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3865 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3867 the_list->add (p, the_list->eval (p));
3868 j->line->add_always_in_view (p);
3869 the_list->add (q, the_list->eval (q));
3870 j->line->add_always_in_view (q);
3873 /* same thing for the end */
3876 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3880 if (j != _lines.end()) {
3881 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3883 /* j is the line that this audio range starts in; fade out of it;
3884 64 samples length plucked out of thin air.
3887 framepos_t b = i->end - 64;
3892 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3893 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
3895 the_list->add (p, the_list->eval (p));
3896 j->line->add_always_in_view (p);
3897 the_list->add (q, the_list->eval (q));
3898 j->line->add_always_in_view (q);
3902 _nothing_to_drag = true;
3904 /* Find all the points that should be dragged and put them in the relevant
3905 points lists in the Line structs.
3908 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3910 uint32_t const N = i->line->npoints ();
3911 for (uint32_t j = 0; j < N; ++j) {
3913 /* here's a control point on this line */
3914 ControlPoint* p = i->line->nth (j);
3915 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
3917 /* see if it's inside a range */
3918 list<AudioRange>::const_iterator k = _ranges.begin ();
3919 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
3923 if (k != _ranges.end()) {
3924 /* dragging this point */
3925 _nothing_to_drag = false;
3926 i->points.push_back (p);
3932 if (_nothing_to_drag) {
3936 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3937 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
3942 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3944 if (_nothing_to_drag) {
3948 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3949 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
3951 /* we are ignoring x position for this drag, so we can just pass in anything */
3952 i->line->drag_motion (0, f, true, false);
3957 AutomationRangeDrag::finished (GdkEvent* event, bool)
3959 if (_nothing_to_drag) {
3963 motion (event, false);
3964 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3965 i->line->end_drag ();
3966 i->line->clear_always_in_view ();
3969 _editor->session()->commit_reversible_command ();
3973 AutomationRangeDrag::aborted ()
3975 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3976 i->line->clear_always_in_view ();
3981 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
3984 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
3985 layer = v->region()->layer ();
3986 initial_y = v->get_canvas_group()->property_y ();
3987 initial_playlist = v->region()->playlist ();