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.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/dB.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/operations.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/session.h"
44 #include "audio_region_view.h"
45 #include "midi_region_view.h"
46 #include "ardour_ui.h"
47 #include "gui_thread.h"
48 #include "control_point.h"
50 #include "region_gain_line.h"
51 #include "editor_drag.h"
52 #include "audio_time_axis.h"
53 #include "midi_time_axis.h"
54 #include "selection.h"
55 #include "midi_selection.h"
56 #include "automation_time_axis.h"
58 #include "editor_cursors.h"
59 #include "mouse_cursors.h"
60 #include "note_base.h"
61 #include "patch_change.h"
62 #include "verbose_cursor.h"
65 using namespace ARDOUR;
68 using namespace Gtkmm2ext;
69 using namespace Editing;
70 using namespace ArdourCanvas;
72 using Gtkmm2ext::Keyboard;
74 double ControlPointDrag::_zero_gain_fraction = -1.0;
76 DragManager::DragManager (Editor* e)
79 , _current_pointer_frame (0)
83 DragManager::~DragManager ()
88 /** Call abort for each active drag */
94 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
99 if (!_drags.empty ()) {
100 _editor->set_follow_playhead (_old_follow_playhead, false);
109 DragManager::add (Drag* d)
111 d->set_manager (this);
112 _drags.push_back (d);
116 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
118 d->set_manager (this);
119 _drags.push_back (d);
124 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
126 /* Prevent follow playhead during the drag to be nice to the user */
127 _old_follow_playhead = _editor->follow_playhead ();
128 _editor->set_follow_playhead (false);
130 _current_pointer_frame = _editor->canvas_event_frame (e, &_current_pointer_x, &_current_pointer_y);
132 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
133 (*i)->start_grab (e, c);
137 /** Call end_grab for each active drag.
138 * @return true if any drag reported movement having occurred.
141 DragManager::end_grab (GdkEvent* e)
146 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
147 bool const t = (*i)->end_grab (e);
158 _editor->set_follow_playhead (_old_follow_playhead, false);
164 DragManager::mark_double_click ()
166 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
167 (*i)->set_double_click (true);
172 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
176 _current_pointer_frame = _editor->canvas_event_frame (e, &_current_pointer_x, &_current_pointer_y);
178 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
179 bool const t = (*i)->motion_handler (e, from_autoscroll);
190 DragManager::window_motion_handler (GdkEvent* e, bool from_autoscroll)
194 _current_pointer_frame = _editor->window_event_frame (e, &_current_pointer_x, &_current_pointer_y);
196 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
197 bool const t = (*i)->motion_handler (e, from_autoscroll);
208 DragManager::have_item (ArdourCanvas::Item* i) const
210 list<Drag*>::const_iterator j = _drags.begin ();
211 while (j != _drags.end() && (*j)->item () != i) {
215 return j != _drags.end ();
218 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
221 , _pointer_frame_offset (0)
222 , _move_threshold_passed (false)
223 , _was_double_click (false)
224 , _raw_grab_frame (0)
226 , _last_pointer_frame (0)
232 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
245 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
247 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
249 if (Keyboard::is_button2_event (&event->button)) {
250 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
251 _y_constrained = true;
252 _x_constrained = false;
254 _y_constrained = false;
255 _x_constrained = true;
258 _x_constrained = false;
259 _y_constrained = false;
262 _raw_grab_frame = _editor->canvas_event_frame (event, &_grab_x, &_grab_y);
263 setup_pointer_frame_offset ();
264 _grab_frame = adjusted_frame (_raw_grab_frame, event);
265 _last_pointer_frame = _grab_frame;
266 _last_pointer_x = _grab_x;
267 _last_pointer_y = _grab_y;
273 /* CAIROCANVAS need a variant here that passes *cursor */
278 if (_editor->session() && _editor->session()->transport_rolling()) {
281 _was_rolling = false;
284 switch (_editor->snap_type()) {
285 case SnapToRegionStart:
286 case SnapToRegionEnd:
287 case SnapToRegionSync:
288 case SnapToRegionBoundary:
289 _editor->build_region_boundary_cache ();
296 /** Call to end a drag `successfully'. Ungrabs item and calls
297 * subclass' finished() method.
299 * @param event GDK event, or 0.
300 * @return true if some movement occurred, otherwise false.
303 Drag::end_grab (GdkEvent* event)
305 _editor->stop_canvas_autoscroll ();
309 finished (event, _move_threshold_passed);
311 _editor->verbose_cursor()->hide ();
313 return _move_threshold_passed;
317 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
321 if (f > _pointer_frame_offset) {
322 pos = f - _pointer_frame_offset;
326 _editor->snap_to_with_modifier (pos, event);
333 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
335 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
339 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
341 /* check to see if we have moved in any way that matters since the last motion event */
342 if (_move_threshold_passed &&
343 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
344 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
348 pair<framecnt_t, int> const threshold = move_threshold ();
350 bool const old_move_threshold_passed = _move_threshold_passed;
352 if (!from_autoscroll && !_move_threshold_passed) {
354 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
355 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
357 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
360 if (active (_editor->mouse_mode) && _move_threshold_passed) {
362 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
363 if (!from_autoscroll) {
364 bool const moving_left = _drags->current_pointer_x() < _last_pointer_x;
365 bool const moving_up = _drags->current_pointer_y() < _last_pointer_y;
366 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), moving_left, moving_up);
369 motion (event, _move_threshold_passed != old_move_threshold_passed);
371 _last_pointer_x = _drags->current_pointer_x ();
372 _last_pointer_y = _drags->current_pointer_y ();
373 _last_pointer_frame = adjusted_current_frame (event);
381 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
389 aborted (_move_threshold_passed);
391 _editor->stop_canvas_autoscroll ();
392 _editor->verbose_cursor()->hide ();
396 Drag::show_verbose_cursor_time (framepos_t frame)
398 _editor->verbose_cursor()->set_time (
400 _drags->current_pointer_x() + 10,
401 _drags->current_pointer_y() + 10
404 _editor->verbose_cursor()->show ();
408 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
410 _editor->verbose_cursor()->show (xoffset);
412 _editor->verbose_cursor()->set_duration (
414 _drags->current_pointer_x() + 10,
415 _drags->current_pointer_y() + 10
420 Drag::show_verbose_cursor_text (string const & text)
422 _editor->verbose_cursor()->show ();
424 _editor->verbose_cursor()->set (
426 _drags->current_pointer_x() + 10,
427 _drags->current_pointer_y() + 10
431 boost::shared_ptr<Region>
432 Drag::add_midi_region (MidiTimeAxisView* view)
434 if (_editor->session()) {
435 const TempoMap& map (_editor->session()->tempo_map());
436 framecnt_t pos = grab_frame();
437 const Meter& m = map.meter_at (pos);
438 /* not that the frame rate used here can be affected by pull up/down which
441 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
442 return view->add_region (grab_frame(), len, true);
445 return boost::shared_ptr<Region>();
448 struct EditorOrderTimeAxisViewSorter {
449 bool operator() (TimeAxisView* a, TimeAxisView* b) {
450 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
451 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
453 return ra->route()->order_key (EditorSort) < rb->route()->order_key (EditorSort);
457 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
461 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
463 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
464 as some of the regions we are dragging may be on such tracks.
467 TrackViewList track_views = _editor->track_views;
468 track_views.sort (EditorOrderTimeAxisViewSorter ());
470 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
471 _time_axis_views.push_back (*i);
473 TimeAxisView::Children children_list = (*i)->get_child_list ();
474 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
475 _time_axis_views.push_back (j->get());
479 /* the list of views can be empty at this point if this is a region list-insert drag
482 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
483 _views.push_back (DraggingView (*i, this));
486 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
490 RegionDrag::region_going_away (RegionView* v)
492 list<DraggingView>::iterator i = _views.begin ();
493 while (i != _views.end() && i->view != v) {
497 if (i != _views.end()) {
502 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
503 * or -1 if it is not found.
506 RegionDrag::find_time_axis_view (TimeAxisView* t) const
509 int const N = _time_axis_views.size ();
510 while (i < N && _time_axis_views[i] != t) {
521 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
522 : RegionDrag (e, i, p, v)
525 , _last_pointer_time_axis_view (0)
526 , _last_pointer_layer (0)
528 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
532 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
534 Drag::start_grab (event, cursor);
536 show_verbose_cursor_time (_last_frame_position);
538 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
540 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
541 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
546 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
548 /* compute the amount of pointer motion in frames, and where
549 the region would be if we moved it by that much.
551 *pending_region_position = adjusted_current_frame (event);
553 framepos_t sync_frame;
554 framecnt_t sync_offset;
557 sync_offset = _primary->region()->sync_offset (sync_dir);
559 /* we don't handle a sync point that lies before zero.
561 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
563 sync_frame = *pending_region_position + (sync_dir*sync_offset);
565 _editor->snap_to_with_modifier (sync_frame, event);
567 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
570 *pending_region_position = _last_frame_position;
573 if (*pending_region_position > max_framepos - _primary->region()->length()) {
574 *pending_region_position = _last_frame_position;
579 /* in locked edit mode, reverse the usual meaning of _x_constrained */
580 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
582 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
584 /* x movement since last time (in pixels) */
585 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
587 /* total x movement */
588 framecnt_t total_dx = *pending_region_position;
589 if (regions_came_from_canvas()) {
590 total_dx = total_dx - grab_frame ();
593 /* check that no regions have gone off the start of the session */
594 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
595 if ((i->view->region()->position() + total_dx) < 0) {
597 *pending_region_position = _last_frame_position;
602 _last_frame_position = *pending_region_position;
609 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
611 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
612 int const n = i->time_axis_view + delta_track;
613 if (n < 0 || n >= int (_time_axis_views.size())) {
614 /* off the top or bottom track */
618 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
619 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
620 /* not a track, or the wrong type */
624 double const l = i->layer + delta_layer;
626 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
627 mode to allow the user to place a region below another on layer 0.
629 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
630 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
631 If it has, the layers will be munged later anyway, so it's ok.
637 /* all regions being dragged are ok with this change */
642 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
644 assert (!_views.empty ());
646 /* Find the TimeAxisView that the pointer is now over */
647 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
649 /* Bail early if we're not over a track */
650 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
652 if (!rtv || !rtv->is_track()) {
653 _editor->verbose_cursor()->hide ();
657 if (first_move && tv.first->view()->layer_display() == Stacked) {
658 tv.first->view()->set_layer_display (Expanded);
661 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
663 /* Here's the current pointer position in terms of time axis view and layer */
664 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
665 double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
667 /* Work out the change in x */
668 framepos_t pending_region_position;
669 double const x_delta = compute_x_delta (event, &pending_region_position);
671 /* Work out the change in y */
673 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
674 double delta_layer = current_pointer_layer - _last_pointer_layer;
676 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
677 /* this y movement is not allowed, so do no y movement this time */
678 delta_time_axis_view = 0;
682 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
683 /* haven't reached next snap point, and we're not switching
684 trackviews nor layers. nothing to do.
689 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
691 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
693 RegionView* rv = i->view;
695 if (rv->region()->locked() || rv->region()->video_locked()) {
703 /* Reparent to a non scrolling group so that we can keep the
704 region selection above all time axis views.
705 Reparenting means that we will have to move the region view
706 within its new parent, as the two parent groups have different coordinates.
709 ArdourCanvas::Group* rvg = rv->get_canvas_group();
710 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
712 rv->get_canvas_group()->reparent (_editor->_region_motion_group);
714 rv->fake_set_opaque (true);
715 rvg->set_position (rv_canvas_offset);
718 /* If we have moved tracks, we'll fudge the layer delta so that the
719 region gets moved back onto layer 0 on its new track; this avoids
720 confusion when dragging regions from non-zero layers onto different
723 double this_delta_layer = delta_layer;
724 if (delta_time_axis_view != 0) {
725 this_delta_layer = - i->layer;
728 /* The TimeAxisView that this region is now on */
729 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
731 /* Ensure it is moved from stacked -> expanded if appropriate */
732 if (tv->view()->layer_display() == Stacked) {
733 tv->view()->set_layer_display (Expanded);
736 /* We're only allowed to go -ve in layer on Expanded views */
737 if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
738 this_delta_layer = - i->layer;
742 rv->set_height (tv->view()->child_height ());
744 /* Update show/hidden status as the region view may have come from a hidden track,
745 or have moved to one.
748 rv->get_canvas_group()->hide ();
750 rv->get_canvas_group()->show ();
753 /* Update the DraggingView */
754 i->time_axis_view += delta_time_axis_view;
755 i->layer += this_delta_layer;
758 _editor->mouse_brush_insert_region (rv, pending_region_position);
763 /* Get the y coordinate of the top of the track that this region is now on */
764 tv->canvas_display()->item_to_canvas (x, y);
766 /* And adjust for the layer that it should be on */
767 StreamView* cv = tv->view ();
768 switch (cv->layer_display ()) {
772 y += (cv->layers() - i->layer - 1) * cv->child_height ();
775 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
779 /* Now move the region view */
780 rv->move (x_delta, y - rv->get_canvas_group()->position().y);
783 } /* foreach region */
785 _total_x_delta += x_delta;
787 if (x_delta != 0 && !_brushing) {
788 show_verbose_cursor_time (_last_frame_position);
791 _last_pointer_time_axis_view += delta_time_axis_view;
792 _last_pointer_layer += delta_layer;
796 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
798 if (_copy && first_move) {
800 /* duplicate the regionview(s) and region(s) */
802 list<DraggingView> new_regionviews;
804 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
806 RegionView* rv = i->view;
807 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
808 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
810 const boost::shared_ptr<const Region> original = rv->region();
811 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
812 region_copy->set_position (original->position());
816 boost::shared_ptr<AudioRegion> audioregion_copy
817 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
819 nrv = new AudioRegionView (*arv, audioregion_copy);
821 boost::shared_ptr<MidiRegion> midiregion_copy
822 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
823 nrv = new MidiRegionView (*mrv, midiregion_copy);
828 nrv->get_canvas_group()->show ();
829 new_regionviews.push_back (DraggingView (nrv, this));
831 /* swap _primary to the copy */
833 if (rv == _primary) {
837 /* ..and deselect the one we copied */
839 rv->set_selected (false);
842 if (!new_regionviews.empty()) {
844 /* reflect the fact that we are dragging the copies */
846 _views = new_regionviews;
848 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
852 RegionMotionDrag::motion (event, first_move);
856 RegionMotionDrag::finished (GdkEvent *, bool)
858 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
863 if ((*i)->view()->layer_display() == Expanded) {
864 (*i)->view()->set_layer_display (Stacked);
870 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
872 RegionMotionDrag::finished (ev, movement_occurred);
874 if (!movement_occurred) {
879 /* reverse this here so that we have the correct logic to finalize
883 if (Config->get_edit_mode() == Lock) {
884 _x_constrained = !_x_constrained;
887 assert (!_views.empty ());
889 /* We might have hidden region views so that they weren't visible during the drag
890 (when they have been reparented). Now everything can be shown again, as region
891 views are back in their track parent groups.
893 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
894 i->view->get_canvas_group()->show ();
897 bool const changed_position = (_last_frame_position != _primary->region()->position());
898 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
899 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
919 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
923 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
925 RegionSelection new_views;
926 PlaylistSet modified_playlists;
927 list<RegionView*> views_to_delete;
930 /* all changes were made during motion event handlers */
932 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
936 _editor->commit_reversible_command ();
940 if (_x_constrained) {
941 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
943 _editor->begin_reversible_command (Operations::region_copy);
946 /* insert the regions into their new playlists */
947 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
949 if (i->view->region()->locked() || i->view->region()->video_locked()) {
955 if (changed_position && !_x_constrained) {
956 where = i->view->region()->position() - drag_delta;
958 where = i->view->region()->position();
961 RegionView* new_view = insert_region_into_playlist (
962 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
969 new_views.push_back (new_view);
971 /* we don't need the copied RegionView any more */
972 views_to_delete.push_back (i->view);
975 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
976 because when views are deleted they are automagically removed from _views, which messes
979 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
983 /* If we've created new regions either by copying or moving
984 to a new track, we want to replace the old selection with the new ones
987 if (new_views.size() > 0) {
988 _editor->selection->set (new_views);
991 /* write commands for the accumulated diffs for all our modified playlists */
992 add_stateful_diff_commands_for_playlists (modified_playlists);
994 _editor->commit_reversible_command ();
998 RegionMoveDrag::finished_no_copy (
999 bool const changed_position,
1000 bool const changed_tracks,
1001 framecnt_t const drag_delta
1004 RegionSelection new_views;
1005 PlaylistSet modified_playlists;
1006 PlaylistSet frozen_playlists;
1007 set<RouteTimeAxisView*> views_to_update;
1010 /* all changes were made during motion event handlers */
1011 _editor->commit_reversible_command ();
1015 if (_x_constrained) {
1016 _editor->begin_reversible_command (_("fixed time region drag"));
1018 _editor->begin_reversible_command (Operations::region_drag);
1021 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1023 RegionView* rv = i->view;
1025 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1026 double const dest_layer = i->layer;
1028 if (rv->region()->locked() || rv->region()->video_locked()) {
1033 views_to_update.insert (dest_rtv);
1037 if (changed_position && !_x_constrained) {
1038 where = rv->region()->position() - drag_delta;
1040 where = rv->region()->position();
1043 if (changed_tracks) {
1045 /* insert into new playlist */
1047 RegionView* new_view = insert_region_into_playlist (
1048 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1051 if (new_view == 0) {
1056 new_views.push_back (new_view);
1058 /* remove from old playlist */
1060 /* the region that used to be in the old playlist is not
1061 moved to the new one - we use a copy of it. as a result,
1062 any existing editor for the region should no longer be
1065 rv->hide_region_editor();
1066 rv->fake_set_opaque (false);
1068 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1072 rv->region()->clear_changes ();
1075 motion on the same track. plonk the previously reparented region
1076 back to its original canvas group (its streamview).
1077 No need to do anything for copies as they are fake regions which will be deleted.
1080 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1081 rv->get_canvas_group()->set_y_position (i->initial_y);
1084 /* just change the model */
1086 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1088 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1089 playlist->set_layer (rv->region(), dest_layer);
1092 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1094 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1097 playlist->freeze ();
1100 /* this movement may result in a crossfade being modified, so we need to get undo
1101 data from the playlist as well as the region.
1104 r = modified_playlists.insert (playlist);
1106 playlist->clear_changes ();
1109 rv->region()->set_position (where);
1111 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1114 if (changed_tracks) {
1116 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1117 was selected in all of them, then removing it from a playlist will have removed all
1118 trace of it from _views (i.e. there were N regions selected, we removed 1,
1119 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1120 corresponding regionview, and _views is now empty).
1122 This could have invalidated any and all iterators into _views.
1124 The heuristic we use here is: if the region selection is empty, break out of the loop
1125 here. if the region selection is not empty, then restart the loop because we know that
1126 we must have removed at least the region(view) we've just been working on as well as any
1127 that we processed on previous iterations.
1129 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1130 we can just iterate.
1134 if (_views.empty()) {
1145 /* If we've created new regions either by copying or moving
1146 to a new track, we want to replace the old selection with the new ones
1149 if (new_views.size() > 0) {
1150 _editor->selection->set (new_views);
1153 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1157 /* write commands for the accumulated diffs for all our modified playlists */
1158 add_stateful_diff_commands_for_playlists (modified_playlists);
1160 _editor->commit_reversible_command ();
1162 /* We have futzed with the layering of canvas items on our streamviews.
1163 If any region changed layer, this will have resulted in the stream
1164 views being asked to set up their region views, and all will be well.
1165 If not, we might now have badly-ordered region views. Ask the StreamViews
1166 involved to sort themselves out, just in case.
1169 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1170 (*i)->view()->playlist_layered ((*i)->track ());
1174 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1175 * @param region Region to remove.
1176 * @param playlist playlist To remove from.
1177 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1178 * that clear_changes () is only called once per playlist.
1181 RegionMoveDrag::remove_region_from_playlist (
1182 boost::shared_ptr<Region> region,
1183 boost::shared_ptr<Playlist> playlist,
1184 PlaylistSet& modified_playlists
1187 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1190 playlist->clear_changes ();
1193 playlist->remove_region (region);
1197 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1198 * clearing the playlist's diff history first if necessary.
1199 * @param region Region to insert.
1200 * @param dest_rtv Destination RouteTimeAxisView.
1201 * @param dest_layer Destination layer.
1202 * @param where Destination position.
1203 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1204 * that clear_changes () is only called once per playlist.
1205 * @return New RegionView, or 0 if no insert was performed.
1208 RegionMoveDrag::insert_region_into_playlist (
1209 boost::shared_ptr<Region> region,
1210 RouteTimeAxisView* dest_rtv,
1213 PlaylistSet& modified_playlists
1216 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1217 if (!dest_playlist) {
1221 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1222 _new_region_view = 0;
1223 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1225 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1226 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1228 dest_playlist->clear_changes ();
1231 dest_playlist->add_region (region, where);
1233 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1234 dest_playlist->set_layer (region, dest_layer);
1239 assert (_new_region_view);
1241 return _new_region_view;
1245 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1247 _new_region_view = rv;
1251 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1253 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1254 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1256 _editor->session()->add_command (c);
1265 RegionMoveDrag::aborted (bool movement_occurred)
1269 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1276 RegionMotionDrag::aborted (movement_occurred);
1281 RegionMotionDrag::aborted (bool)
1283 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1285 StreamView* sview = (*i)->view();
1288 if (sview->layer_display() == Expanded) {
1289 sview->set_layer_display (Stacked);
1294 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1295 RegionView* rv = i->view;
1296 TimeAxisView* tv = &(rv->get_time_axis_view ());
1297 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1299 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1300 rv->get_canvas_group()->set_y_position (0);
1302 rv->fake_set_opaque (false);
1303 rv->move (-_total_x_delta, 0);
1304 rv->set_height (rtv->view()->child_height ());
1308 /** @param b true to brush, otherwise false.
1309 * @param c true to make copies of the regions being moved, otherwise false.
1311 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1312 : RegionMotionDrag (e, i, p, v, b),
1315 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1318 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1319 if (rtv && rtv->is_track()) {
1320 speed = rtv->track()->speed ();
1323 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1327 RegionMoveDrag::setup_pointer_frame_offset ()
1329 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1332 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1333 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1335 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1337 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1338 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1340 _primary = v->view()->create_region_view (r, false, false);
1342 _primary->get_canvas_group()->show ();
1343 _primary->set_position (pos, 0);
1344 _views.push_back (DraggingView (_primary, this));
1346 _last_frame_position = pos;
1348 _item = _primary->get_canvas_group ();
1352 RegionInsertDrag::finished (GdkEvent *, bool)
1354 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1356 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1357 _primary->get_canvas_group()->set_y_position (0);
1359 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1361 _editor->begin_reversible_command (Operations::insert_region);
1362 playlist->clear_changes ();
1363 playlist->add_region (_primary->region (), _last_frame_position);
1364 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1365 _editor->commit_reversible_command ();
1373 RegionInsertDrag::aborted (bool)
1380 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1381 : RegionMoveDrag (e, i, p, v, false, false)
1383 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1386 struct RegionSelectionByPosition {
1387 bool operator() (RegionView*a, RegionView* b) {
1388 return a->region()->position () < b->region()->position();
1393 RegionSpliceDrag::motion (GdkEvent* event, bool)
1395 /* Which trackview is this ? */
1397 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1398 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1400 /* The region motion is only processed if the pointer is over
1404 if (!tv || !tv->is_track()) {
1405 /* To make sure we hide the verbose canvas cursor when the mouse is
1406 not held over and audiotrack.
1408 _editor->verbose_cursor()->hide ();
1414 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1420 RegionSelection copy (_editor->selection->regions);
1422 RegionSelectionByPosition cmp;
1425 framepos_t const pf = adjusted_current_frame (event);
1427 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1429 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1435 boost::shared_ptr<Playlist> playlist;
1437 if ((playlist = atv->playlist()) == 0) {
1441 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1446 if (pf < (*i)->region()->last_frame() + 1) {
1450 if (pf > (*i)->region()->first_frame()) {
1456 playlist->shuffle ((*i)->region(), dir);
1461 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1463 RegionMoveDrag::finished (event, movement_occurred);
1467 RegionSpliceDrag::aborted (bool)
1472 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1474 _view (dynamic_cast<MidiTimeAxisView*> (v))
1476 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1482 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1485 _region = add_midi_region (_view);
1486 _view->playlist()->freeze ();
1489 framepos_t const f = adjusted_current_frame (event);
1490 if (f < grab_frame()) {
1491 _region->set_position (f);
1494 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1495 so that if this region is duplicated, its duplicate starts on
1496 a snap point rather than 1 frame after a snap point. Otherwise things get
1497 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1498 place snapped notes at the start of the region.
1501 framecnt_t const len = (framecnt_t) fabs (f - grab_frame () - 1);
1502 _region->set_length (len < 1 ? 1 : len);
1508 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1510 if (!movement_occurred) {
1511 add_midi_region (_view);
1513 _view->playlist()->thaw ();
1518 RegionCreateDrag::aborted (bool)
1521 _view->playlist()->thaw ();
1527 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1531 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1535 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1537 Gdk::Cursor* cursor;
1538 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1540 float x_fraction = cnote->mouse_x_fraction ();
1542 if (x_fraction > 0.0 && x_fraction < 0.25) {
1543 cursor = _editor->cursors()->left_side_trim;
1545 cursor = _editor->cursors()->right_side_trim;
1548 Drag::start_grab (event, cursor);
1550 region = &cnote->region_view();
1552 double const region_start = region->get_position_pixels();
1553 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1555 if (grab_x() <= middle_point) {
1556 cursor = _editor->cursors()->left_side_trim;
1559 cursor = _editor->cursors()->right_side_trim;
1565 if (event->motion.state & Keyboard::PrimaryModifier) {
1571 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1573 if (ms.size() > 1) {
1574 /* has to be relative, may make no sense otherwise */
1578 /* select this note; if it is already selected, preserve the existing selection,
1579 otherwise make this note the only one selected.
1581 region->note_selected (cnote, cnote->selected ());
1583 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1584 MidiRegionSelection::iterator next;
1587 (*r)->begin_resizing (at_front);
1593 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1595 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1596 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1597 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1599 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1604 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1606 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1607 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1608 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1610 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1615 NoteResizeDrag::aborted (bool)
1617 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1618 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1619 (*r)->abort_resizing ();
1623 AVDraggingView::AVDraggingView (RegionView* v)
1626 initial_position = v->region()->position ();
1629 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1632 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1635 TrackViewList empty;
1637 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1638 std::list<RegionView*> views = rs.by_layer();
1640 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1641 RegionView* rv = (*i);
1642 if (!rv->region()->video_locked()) {
1645 _views.push_back (AVDraggingView (rv));
1650 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1652 Drag::start_grab (event);
1653 if (_editor->session() == 0) {
1657 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1658 _max_backwards_drag = (
1659 ARDOUR_UI::instance()->video_timeline->get_duration()
1660 + ARDOUR_UI::instance()->video_timeline->get_offset()
1661 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1664 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1665 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1666 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1669 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1672 Timecode::Time timecode;
1673 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1674 snprintf (buf, sizeof (buf), "Video Start:\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, (_startdrag_video_offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1675 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1676 _editor->verbose_cursor()->show ();
1680 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1682 if (_editor->session() == 0) {
1685 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1689 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1690 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(dt);
1692 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1693 dt = - _max_backwards_drag;
1696 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1697 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1699 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1700 RegionView* rv = i->view;
1701 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1704 rv->fake_set_opaque (true);
1705 rv->region()->clear_changes ();
1706 rv->region()->suspend_property_changes();
1708 rv->region()->set_position(i->initial_position + dt);
1709 rv->region_changed(ARDOUR::Properties::position);
1712 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1713 Timecode::Time timecode;
1714 Timecode::Time timediff;
1716 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1717 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1718 snprintf (buf, sizeof (buf),
1719 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1720 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1721 , _("Video Start:"),
1722 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1724 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1726 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1727 _editor->verbose_cursor()->show ();
1731 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1733 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1737 if (!movement_occurred || ! _editor->session()) {
1741 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1743 _editor->begin_reversible_command (_("Move Video"));
1745 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1746 ARDOUR_UI::instance()->video_timeline->save_undo();
1747 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1748 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1750 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1751 i->view->drag_end();
1752 i->view->fake_set_opaque (false);
1753 i->view->region()->resume_property_changes ();
1755 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1758 _editor->session()->maybe_update_session_range(
1759 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1760 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1764 _editor->commit_reversible_command ();
1768 VideoTimeLineDrag::aborted (bool)
1770 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1773 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1774 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1776 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1777 i->view->region()->resume_property_changes ();
1778 i->view->region()->set_position(i->initial_position);
1782 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1783 : RegionDrag (e, i, p, v)
1785 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1786 _preserve_fade_anchor = preserve_fade_anchor;
1790 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1793 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1794 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1796 if (tv && tv->is_track()) {
1797 speed = tv->track()->speed();
1800 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1801 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1802 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1804 framepos_t const pf = adjusted_current_frame (event);
1806 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1807 /* Move the contents of the region around without changing the region bounds */
1808 _operation = ContentsTrim;
1809 Drag::start_grab (event, _editor->cursors()->trimmer);
1811 /* These will get overridden for a point trim.*/
1812 if (pf < (region_start + region_length/2)) {
1813 /* closer to front */
1814 _operation = StartTrim;
1815 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1818 _operation = EndTrim;
1819 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1823 switch (_operation) {
1825 show_verbose_cursor_time (region_start);
1826 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1827 i->view->trim_front_starting ();
1831 show_verbose_cursor_time (region_end);
1834 show_verbose_cursor_time (pf);
1838 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1839 i->view->region()->suspend_property_changes ();
1844 TrimDrag::motion (GdkEvent* event, bool first_move)
1846 RegionView* rv = _primary;
1849 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1850 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1851 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1852 frameoffset_t frame_delta = 0;
1854 if (tv && tv->is_track()) {
1855 speed = tv->track()->speed();
1858 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1864 switch (_operation) {
1866 trim_type = "Region start trim";
1869 trim_type = "Region end trim";
1872 trim_type = "Region content trim";
1876 _editor->begin_reversible_command (trim_type);
1878 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1879 RegionView* rv = i->view;
1880 rv->fake_set_opaque (false);
1881 rv->enable_display (false);
1882 rv->region()->playlist()->clear_owned_changes ();
1884 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1887 arv->temporarily_hide_envelope ();
1891 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1892 insert_result = _editor->motion_frozen_playlists.insert (pl);
1894 if (insert_result.second) {
1900 bool non_overlap_trim = false;
1902 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1903 non_overlap_trim = true;
1906 switch (_operation) {
1908 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1909 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1910 if (changed && _preserve_fade_anchor) {
1911 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1916 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1917 distance = _drags->current_pointer_x() - grab_x();
1918 len = ar->fade_in()->back()->when;
1919 new_length = len - _editor->pixel_to_sample (distance);
1920 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
1921 arv->reset_fade_in_shape_width (ar, new_length); //the grey shape
1928 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1929 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1930 if (changed && _preserve_fade_anchor) {
1931 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1936 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1937 distance = grab_x() - _drags->current_pointer_x();
1938 len = ar->fade_out()->back()->when;
1939 new_length = len - _editor->pixel_to_sample (distance);
1940 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
1941 arv->reset_fade_out_shape_width (ar, new_length); //the grey shape
1949 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1950 // frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1952 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1953 i->view->move_contents (frame_delta);
1959 switch (_operation) {
1961 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1964 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1967 // show_verbose_cursor_time (frame_delta);
1974 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1976 if (movement_occurred) {
1977 motion (event, false);
1979 if (_operation == StartTrim) {
1980 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1982 /* This must happen before the region's StatefulDiffCommand is created, as it may
1983 `correct' (ahem) the region's _start from being negative to being zero. It
1984 needs to be zero in the undo record.
1986 i->view->trim_front_ending ();
1988 if (_preserve_fade_anchor) {
1989 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1994 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1995 distance = _drags->current_pointer_x() - grab_x();
1996 len = ar->fade_in()->back()->when;
1997 new_length = len - _editor->pixel_to_sample (distance);
1998 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
1999 ar->set_fade_in_length(new_length);
2003 } else if (_operation == EndTrim) {
2004 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2005 if (_preserve_fade_anchor) {
2006 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2011 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2012 distance = _drags->current_pointer_x() - grab_x();
2013 len = ar->fade_out()->back()->when;
2014 new_length = len - _editor->pixel_to_sample (distance);
2015 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2016 ar->set_fade_out_length(new_length);
2022 if (!_views.empty()) {
2023 if (_operation == StartTrim) {
2024 _editor->maybe_locate_with_edit_preroll(
2025 _views.begin()->view->region()->position());
2027 if (_operation == EndTrim) {
2028 _editor->maybe_locate_with_edit_preroll(
2029 _views.begin()->view->region()->position() +
2030 _views.begin()->view->region()->length());
2034 if (!_editor->selection->selected (_primary)) {
2035 _primary->thaw_after_trim ();
2038 set<boost::shared_ptr<Playlist> > diffed_playlists;
2040 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2041 i->view->thaw_after_trim ();
2042 i->view->enable_display (true);
2043 i->view->fake_set_opaque (true);
2045 /* Trimming one region may affect others on the playlist, so we need
2046 to get undo Commands from the whole playlist rather than just the
2047 region. Use diffed_playlists to make sure we don't diff a given
2048 playlist more than once.
2050 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2051 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2052 vector<Command*> cmds;
2054 _editor->session()->add_commands (cmds);
2055 diffed_playlists.insert (p);
2060 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2064 _editor->motion_frozen_playlists.clear ();
2065 _editor->commit_reversible_command();
2068 /* no mouse movement */
2069 _editor->point_trim (event, adjusted_current_frame (event));
2072 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2073 if (_operation == StartTrim) {
2074 i->view->trim_front_ending ();
2077 i->view->region()->resume_property_changes ();
2082 TrimDrag::aborted (bool movement_occurred)
2084 /* Our motion method is changing model state, so use the Undo system
2085 to cancel. Perhaps not ideal, as this will leave an Undo point
2086 behind which may be slightly odd from the user's point of view.
2091 if (movement_occurred) {
2095 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2096 i->view->region()->resume_property_changes ();
2101 TrimDrag::setup_pointer_frame_offset ()
2103 list<DraggingView>::iterator i = _views.begin ();
2104 while (i != _views.end() && i->view != _primary) {
2108 if (i == _views.end()) {
2112 switch (_operation) {
2114 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2117 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2124 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2128 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2129 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2134 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2136 Drag::start_grab (event, cursor);
2137 show_verbose_cursor_time (adjusted_current_frame(event));
2141 MeterMarkerDrag::setup_pointer_frame_offset ()
2143 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2147 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2151 // create a dummy marker for visual representation of moving the
2152 // section, because whether its a copy or not, we're going to
2153 // leave or lose the original marker (leave if its a copy; lose if its
2154 // not, because we'll remove it from the map).
2156 MeterSection section (_marker->meter());
2158 if (!section.movable()) {
2163 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2165 _marker = new MeterMarker (
2167 *_editor->meter_group,
2168 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2170 *new MeterSection (_marker->meter())
2173 /* use the new marker for the grab */
2174 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2177 TempoMap& map (_editor->session()->tempo_map());
2178 /* get current state */
2179 before_state = &map.get_state();
2180 /* remove the section while we drag it */
2181 map.remove_meter (section, true);
2185 framepos_t const pf = adjusted_current_frame (event);
2186 _marker->set_position (pf);
2187 show_verbose_cursor_time (pf);
2191 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2193 if (!movement_occurred) {
2197 motion (event, false);
2199 Timecode::BBT_Time when;
2201 TempoMap& map (_editor->session()->tempo_map());
2202 map.bbt_time (last_pointer_frame(), when);
2204 if (_copy == true) {
2205 _editor->begin_reversible_command (_("copy meter mark"));
2206 XMLNode &before = map.get_state();
2207 map.add_meter (_marker->meter(), when);
2208 XMLNode &after = map.get_state();
2209 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2210 _editor->commit_reversible_command ();
2213 _editor->begin_reversible_command (_("move meter mark"));
2215 /* we removed it before, so add it back now */
2217 map.add_meter (_marker->meter(), when);
2218 XMLNode &after = map.get_state();
2219 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2220 _editor->commit_reversible_command ();
2223 // delete the dummy marker we used for visual representation while moving.
2224 // a new visual marker will show up automatically.
2229 MeterMarkerDrag::aborted (bool moved)
2231 _marker->set_position (_marker->meter().frame ());
2234 TempoMap& map (_editor->session()->tempo_map());
2235 /* we removed it before, so add it back now */
2236 map.add_meter (_marker->meter(), _marker->meter().frame());
2237 // delete the dummy marker we used for visual representation while moving.
2238 // a new visual marker will show up automatically.
2243 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2247 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2249 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2254 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2256 Drag::start_grab (event, cursor);
2257 show_verbose_cursor_time (adjusted_current_frame (event));
2261 TempoMarkerDrag::setup_pointer_frame_offset ()
2263 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2267 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2271 // create a dummy marker for visual representation of moving the
2272 // section, because whether its a copy or not, we're going to
2273 // leave or lose the original marker (leave if its a copy; lose if its
2274 // not, because we'll remove it from the map).
2276 // create a dummy marker for visual representation of moving the copy.
2277 // The actual copying is not done before we reach the finish callback.
2280 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2282 TempoSection section (_marker->tempo());
2284 _marker = new TempoMarker (
2286 *_editor->tempo_group,
2287 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2289 *new TempoSection (_marker->tempo())
2292 /* use the new marker for the grab */
2293 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2296 TempoMap& map (_editor->session()->tempo_map());
2297 /* get current state */
2298 before_state = &map.get_state();
2299 /* remove the section while we drag it */
2300 map.remove_tempo (section, true);
2304 framepos_t const pf = adjusted_current_frame (event);
2305 _marker->set_position (pf);
2306 show_verbose_cursor_time (pf);
2310 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2312 if (!movement_occurred) {
2316 motion (event, false);
2318 TempoMap& map (_editor->session()->tempo_map());
2319 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2320 Timecode::BBT_Time when;
2322 map.bbt_time (beat_time, when);
2324 if (_copy == true) {
2325 _editor->begin_reversible_command (_("copy tempo mark"));
2326 XMLNode &before = map.get_state();
2327 map.add_tempo (_marker->tempo(), when);
2328 XMLNode &after = map.get_state();
2329 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2330 _editor->commit_reversible_command ();
2333 _editor->begin_reversible_command (_("move tempo mark"));
2334 /* we removed it before, so add it back now */
2335 map.add_tempo (_marker->tempo(), when);
2336 XMLNode &after = map.get_state();
2337 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2338 _editor->commit_reversible_command ();
2341 // delete the dummy marker we used for visual representation while moving.
2342 // a new visual marker will show up automatically.
2347 TempoMarkerDrag::aborted (bool moved)
2349 _marker->set_position (_marker->tempo().frame());
2351 TempoMap& map (_editor->session()->tempo_map());
2352 /* we removed it before, so add it back now */
2353 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2354 // delete the dummy marker we used for visual representation while moving.
2355 // a new visual marker will show up automatically.
2360 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2361 : Drag (e, &c.time_bar_canvas_item())
2365 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2368 /** Do all the things we do when dragging the playhead to make it look as though
2369 * we have located, without actually doing the locate (because that would cause
2370 * the diskstream buffers to be refilled, which is too slow).
2373 CursorDrag::fake_locate (framepos_t t)
2375 _editor->playhead_cursor->set_position (t);
2377 Session* s = _editor->session ();
2378 if (s->timecode_transmission_suspended ()) {
2379 framepos_t const f = _editor->playhead_cursor->current_frame ();
2380 s->send_mmc_locate (f);
2381 s->send_full_time_code (f);
2384 show_verbose_cursor_time (t);
2385 _editor->UpdateAllTransportClocks (t);
2389 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2391 Drag::start_grab (event, c);
2393 _grab_zoom = _editor->samples_per_pixel;
2395 framepos_t where = _editor->canvas_event_frame (event, 0, 0);
2397 _editor->snap_to_with_modifier (where, event);
2399 _editor->_dragging_playhead = true;
2401 Session* s = _editor->session ();
2403 /* grab the track canvas item as well */
2405 _cursor.track_canvas_item().grab();
2408 if (_was_rolling && _stop) {
2412 if (s->is_auditioning()) {
2413 s->cancel_audition ();
2417 if (AudioEngine::instance()->connected()) {
2419 /* do this only if we're the engine is connected
2420 * because otherwise this request will never be
2421 * serviced and we'll busy wait forever. likewise,
2422 * notice if we are disconnected while waiting for the
2423 * request to be serviced.
2426 s->request_suspend_timecode_transmission ();
2427 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2428 /* twiddle our thumbs */
2433 fake_locate (where);
2437 CursorDrag::motion (GdkEvent* event, bool)
2439 framepos_t const adjusted_frame = adjusted_current_frame (event);
2440 if (adjusted_frame != last_pointer_frame()) {
2441 fake_locate (adjusted_frame);
2446 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2448 _editor->_dragging_playhead = false;
2450 _cursor.track_canvas_item().ungrab();
2452 if (!movement_occurred && _stop) {
2456 motion (event, false);
2458 Session* s = _editor->session ();
2460 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2461 _editor->_pending_locate_request = true;
2462 s->request_resume_timecode_transmission ();
2467 CursorDrag::aborted (bool)
2469 _cursor.track_canvas_item().ungrab();
2471 if (_editor->_dragging_playhead) {
2472 _editor->session()->request_resume_timecode_transmission ();
2473 _editor->_dragging_playhead = false;
2476 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2479 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2480 : RegionDrag (e, i, p, v)
2482 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2486 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2488 Drag::start_grab (event, cursor);
2490 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2491 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2493 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2497 FadeInDrag::setup_pointer_frame_offset ()
2499 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2500 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2501 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2505 FadeInDrag::motion (GdkEvent* event, bool)
2507 framecnt_t fade_length;
2509 framepos_t const pos = adjusted_current_frame (event);
2511 boost::shared_ptr<Region> region = _primary->region ();
2513 if (pos < (region->position() + 64)) {
2514 fade_length = 64; // this should be a minimum defined somewhere
2515 } else if (pos > region->last_frame()) {
2516 fade_length = region->length();
2518 fade_length = pos - region->position();
2521 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2523 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2529 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2532 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2536 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2538 if (!movement_occurred) {
2542 framecnt_t fade_length;
2544 framepos_t const pos = adjusted_current_frame (event);
2546 boost::shared_ptr<Region> region = _primary->region ();
2548 if (pos < (region->position() + 64)) {
2549 fade_length = 64; // this should be a minimum defined somewhere
2550 } else if (pos > region->last_frame()) {
2551 fade_length = region->length();
2553 fade_length = pos - region->position();
2556 _editor->begin_reversible_command (_("change fade in length"));
2558 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2560 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2566 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2567 XMLNode &before = alist->get_state();
2569 tmp->audio_region()->set_fade_in_length (fade_length);
2570 tmp->audio_region()->set_fade_in_active (true);
2572 XMLNode &after = alist->get_state();
2573 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2576 _editor->commit_reversible_command ();
2580 FadeInDrag::aborted (bool)
2582 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2583 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2589 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2593 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2594 : RegionDrag (e, i, p, v)
2596 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2600 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2602 Drag::start_grab (event, cursor);
2604 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2605 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2607 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2611 FadeOutDrag::setup_pointer_frame_offset ()
2613 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2614 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2615 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2619 FadeOutDrag::motion (GdkEvent* event, bool)
2621 framecnt_t fade_length;
2623 framepos_t const pos = adjusted_current_frame (event);
2625 boost::shared_ptr<Region> region = _primary->region ();
2627 if (pos > (region->last_frame() - 64)) {
2628 fade_length = 64; // this should really be a minimum fade defined somewhere
2630 else if (pos < region->position()) {
2631 fade_length = region->length();
2634 fade_length = region->last_frame() - pos;
2637 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2639 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2645 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2648 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2652 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2654 if (!movement_occurred) {
2658 framecnt_t fade_length;
2660 framepos_t const pos = adjusted_current_frame (event);
2662 boost::shared_ptr<Region> region = _primary->region ();
2664 if (pos > (region->last_frame() - 64)) {
2665 fade_length = 64; // this should really be a minimum fade defined somewhere
2667 else if (pos < region->position()) {
2668 fade_length = region->length();
2671 fade_length = region->last_frame() - pos;
2674 _editor->begin_reversible_command (_("change fade out length"));
2676 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2678 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2684 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2685 XMLNode &before = alist->get_state();
2687 tmp->audio_region()->set_fade_out_length (fade_length);
2688 tmp->audio_region()->set_fade_out_active (true);
2690 XMLNode &after = alist->get_state();
2691 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2694 _editor->commit_reversible_command ();
2698 FadeOutDrag::aborted (bool)
2700 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2701 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2707 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2711 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2714 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2716 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2719 _points.push_back (ArdourCanvas::Duple (0, 0));
2720 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2723 MarkerDrag::~MarkerDrag ()
2725 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2730 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2732 location = new Location (*l);
2733 markers.push_back (m);
2738 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2740 Drag::start_grab (event, cursor);
2744 Location *location = _editor->find_location_from_marker (_marker, is_start);
2745 _editor->_dragging_edit_point = true;
2747 update_item (location);
2749 // _drag_line->show();
2750 // _line->raise_to_top();
2753 show_verbose_cursor_time (location->start());
2755 show_verbose_cursor_time (location->end());
2758 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2761 case Selection::Toggle:
2762 /* we toggle on the button release */
2764 case Selection::Set:
2765 if (!_editor->selection->selected (_marker)) {
2766 _editor->selection->set (_marker);
2769 case Selection::Extend:
2771 Locations::LocationList ll;
2772 list<Marker*> to_add;
2774 _editor->selection->markers.range (s, e);
2775 s = min (_marker->position(), s);
2776 e = max (_marker->position(), e);
2779 if (e < max_framepos) {
2782 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2783 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2784 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2787 to_add.push_back (lm->start);
2790 to_add.push_back (lm->end);
2794 if (!to_add.empty()) {
2795 _editor->selection->add (to_add);
2799 case Selection::Add:
2800 _editor->selection->add (_marker);
2804 /* Set up copies for us to manipulate during the drag
2807 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2809 Location* l = _editor->find_location_from_marker (*i, is_start);
2816 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2818 /* range: check that the other end of the range isn't
2821 CopiedLocationInfo::iterator x;
2822 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2823 if (*(*x).location == *l) {
2827 if (x == _copied_locations.end()) {
2828 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2830 (*x).markers.push_back (*i);
2831 (*x).move_both = true;
2839 MarkerDrag::setup_pointer_frame_offset ()
2842 Location *location = _editor->find_location_from_marker (_marker, is_start);
2843 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2847 MarkerDrag::motion (GdkEvent* event, bool)
2849 framecnt_t f_delta = 0;
2851 bool move_both = false;
2852 Location *real_location;
2853 Location *copy_location = 0;
2855 framepos_t const newframe = adjusted_current_frame (event);
2856 framepos_t next = newframe;
2858 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2862 CopiedLocationInfo::iterator x;
2864 /* find the marker we're dragging, and compute the delta */
2866 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2868 copy_location = (*x).location;
2870 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2872 /* this marker is represented by this
2873 * CopiedLocationMarkerInfo
2876 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
2881 if (real_location->is_mark()) {
2882 f_delta = newframe - copy_location->start();
2886 switch (_marker->type()) {
2887 case Marker::SessionStart:
2888 case Marker::RangeStart:
2889 case Marker::LoopStart:
2890 case Marker::PunchIn:
2891 f_delta = newframe - copy_location->start();
2894 case Marker::SessionEnd:
2895 case Marker::RangeEnd:
2896 case Marker::LoopEnd:
2897 case Marker::PunchOut:
2898 f_delta = newframe - copy_location->end();
2901 /* what kind of marker is this ? */
2910 if (x == _copied_locations.end()) {
2911 /* hmm, impossible - we didn't find the dragged marker */
2915 /* now move them all */
2917 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2919 copy_location = x->location;
2921 /* call this to find out if its the start or end */
2923 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
2927 if (real_location->locked()) {
2931 if (copy_location->is_mark()) {
2935 copy_location->set_start (copy_location->start() + f_delta);
2939 framepos_t new_start = copy_location->start() + f_delta;
2940 framepos_t new_end = copy_location->end() + f_delta;
2942 if (is_start) { // start-of-range marker
2944 if (move_both || (*x).move_both) {
2945 copy_location->set_start (new_start);
2946 copy_location->set_end (new_end);
2947 } else if (new_start < copy_location->end()) {
2948 copy_location->set_start (new_start);
2949 } else if (newframe > 0) {
2950 _editor->snap_to (next, 1, true);
2951 copy_location->set_end (next);
2952 copy_location->set_start (newframe);
2955 } else { // end marker
2957 if (move_both || (*x).move_both) {
2958 copy_location->set_end (new_end);
2959 copy_location->set_start (new_start);
2960 } else if (new_end > copy_location->start()) {
2961 copy_location->set_end (new_end);
2962 } else if (newframe > 0) {
2963 _editor->snap_to (next, -1, true);
2964 copy_location->set_start (next);
2965 copy_location->set_end (newframe);
2970 update_item (copy_location);
2972 /* now lookup the actual GUI items used to display this
2973 * location and move them to wherever the copy of the location
2974 * is now. This means that the logic in ARDOUR::Location is
2975 * still enforced, even though we are not (yet) modifying
2976 * the real Location itself.
2979 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2982 lm->set_position (copy_location->start(), copy_location->end());
2987 assert (!_copied_locations.empty());
2989 show_verbose_cursor_time (newframe);
2993 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2995 if (!movement_occurred) {
2997 if (was_double_click()) {
2998 cerr << "End of marker double click\n";
3001 /* just a click, do nothing but finish
3002 off the selection process
3005 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3008 case Selection::Set:
3009 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3010 _editor->selection->set (_marker);
3014 case Selection::Toggle:
3015 /* we toggle on the button release, click only */
3016 _editor->selection->toggle (_marker);
3019 case Selection::Extend:
3020 case Selection::Add:
3027 _editor->_dragging_edit_point = false;
3029 _editor->begin_reversible_command ( _("move marker") );
3030 XMLNode &before = _editor->session()->locations()->get_state();
3032 MarkerSelection::iterator i;
3033 CopiedLocationInfo::iterator x;
3036 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3037 x != _copied_locations.end() && i != _editor->selection->markers.end();
3040 Location * location = _editor->find_location_from_marker (*i, is_start);
3044 if (location->locked()) {
3048 if (location->is_mark()) {
3049 location->set_start (((*x).location)->start());
3051 location->set (((*x).location)->start(), ((*x).location)->end());
3056 XMLNode &after = _editor->session()->locations()->get_state();
3057 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3058 _editor->commit_reversible_command ();
3062 MarkerDrag::aborted (bool)
3068 MarkerDrag::update_item (Location*)
3073 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3075 _cumulative_x_drag (0),
3076 _cumulative_y_drag (0)
3078 if (_zero_gain_fraction < 0.0) {
3079 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3082 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3084 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3090 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3092 Drag::start_grab (event, _editor->cursors()->fader);
3094 // start the grab at the center of the control point so
3095 // the point doesn't 'jump' to the mouse after the first drag
3096 _fixed_grab_x = _point->get_x();
3097 _fixed_grab_y = _point->get_y();
3099 float const fraction = 1 - (_point->get_y() / _point->line().height());
3101 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3103 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3104 event->button.x + 10, event->button.y + 10);
3106 _editor->verbose_cursor()->show ();
3108 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3110 if (!_point->can_slide ()) {
3111 _x_constrained = true;
3116 ControlPointDrag::motion (GdkEvent* event, bool)
3118 double dx = _drags->current_pointer_x() - last_pointer_x();
3119 double dy = _drags->current_pointer_y() - last_pointer_y();
3121 if (event->button.state & Keyboard::SecondaryModifier) {
3126 /* coordinate in pixels relative to the start of the region (for region-based automation)
3127 or track (for track-based automation) */
3128 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3129 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3131 // calculate zero crossing point. back off by .01 to stay on the
3132 // positive side of zero
3133 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3135 // make sure we hit zero when passing through
3136 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3140 if (_x_constrained) {
3143 if (_y_constrained) {
3147 _cumulative_x_drag = cx - _fixed_grab_x;
3148 _cumulative_y_drag = cy - _fixed_grab_y;
3152 cy = min ((double) _point->line().height(), cy);
3154 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3156 if (!_x_constrained) {
3157 _editor->snap_to_with_modifier (cx_frames, event);
3160 cx_frames = min (cx_frames, _point->line().maximum_time());
3162 float const fraction = 1.0 - (cy / _point->line().height());
3164 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3166 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3170 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3172 if (!movement_occurred) {
3176 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3177 _editor->reset_point_selection ();
3181 motion (event, false);
3184 _point->line().end_drag (_pushing, _final_index);
3185 _editor->session()->commit_reversible_command ();
3189 ControlPointDrag::aborted (bool)
3191 _point->line().reset ();
3195 ControlPointDrag::active (Editing::MouseMode m)
3197 if (m == Editing::MouseGain) {
3198 /* always active in mouse gain */
3202 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3203 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3206 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3209 _cumulative_y_drag (0)
3211 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3215 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3217 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3220 _item = &_line->grab_item ();
3222 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3223 origin, and ditto for y.
3226 double cx = event->button.x;
3227 double cy = event->button.y;
3229 _line->parent_group().canvas_to_item (cx, cy);
3231 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3236 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3237 /* no adjacent points */
3241 Drag::start_grab (event, _editor->cursors()->fader);
3243 /* store grab start in parent frame */
3248 double fraction = 1.0 - (cy / _line->height());
3250 _line->start_drag_line (before, after, fraction);
3252 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3253 event->button.x + 10, event->button.y + 10);
3255 _editor->verbose_cursor()->show ();
3259 LineDrag::motion (GdkEvent* event, bool)
3261 double dy = _drags->current_pointer_y() - last_pointer_y();
3263 if (event->button.state & Keyboard::SecondaryModifier) {
3267 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3269 _cumulative_y_drag = cy - _fixed_grab_y;
3272 cy = min ((double) _line->height(), cy);
3274 double const fraction = 1.0 - (cy / _line->height());
3277 /* we are ignoring x position for this drag, so we can just pass in anything */
3278 _line->drag_motion (0, fraction, true, false, ignored);
3280 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3284 LineDrag::finished (GdkEvent* event, bool)
3286 motion (event, false);
3287 _line->end_drag (false, 0);
3288 _editor->session()->commit_reversible_command ();
3292 LineDrag::aborted (bool)
3297 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3300 _cumulative_x_drag (0)
3302 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3306 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3308 Drag::start_grab (event);
3310 _line = reinterpret_cast<Line*> (_item);
3313 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3315 double cx = event->button.x;
3316 double cy = event->button.y;
3318 _item->parent()->canvas_to_item (cx, cy);
3320 /* store grab start in parent frame */
3321 _region_view_grab_x = cx;
3323 _before = *(float*) _item->get_data ("position");
3325 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3327 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3331 FeatureLineDrag::motion (GdkEvent*, bool)
3333 double dx = _drags->current_pointer_x() - last_pointer_x();
3335 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3337 _cumulative_x_drag += dx;
3339 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3348 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3350 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3352 float *pos = new float;
3355 _line->set_data ("position", pos);
3361 FeatureLineDrag::finished (GdkEvent*, bool)
3363 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3364 _arv->update_transient(_before, _before);
3368 FeatureLineDrag::aborted (bool)
3373 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3375 , _vertical_only (false)
3377 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3381 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3383 Drag::start_grab (event);
3384 show_verbose_cursor_time (adjusted_current_frame (event));
3388 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3395 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3397 framepos_t grab = grab_frame ();
3398 if (Config->get_rubberbanding_snaps_to_grid ()) {
3399 _editor->snap_to_with_modifier (grab, event);
3402 /* base start and end on initial click position */
3412 if (_drags->current_pointer_y() < grab_y()) {
3413 y1 = _drags->current_pointer_y();
3416 y2 = _drags->current_pointer_y();
3421 if (start != end || y1 != y2) {
3423 double x1 = _editor->sample_to_pixel (start);
3424 double x2 = _editor->sample_to_pixel (end);
3426 _editor->rubberband_rect->set_x0 (x1);
3427 if (_vertical_only) {
3428 /* fixed 10 pixel width */
3429 _editor->rubberband_rect->set_x1 (x1 + 10);
3431 _editor->rubberband_rect->set_x1 (x2);
3434 _editor->rubberband_rect->set_y0 (y1);
3435 _editor->rubberband_rect->set_y1 (y2);
3437 _editor->rubberband_rect->show();
3438 _editor->rubberband_rect->raise_to_top();
3440 show_verbose_cursor_time (pf);
3442 do_select_things (event, true);
3447 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3452 if (grab_frame() < last_pointer_frame()) {
3454 x2 = last_pointer_frame ();
3457 x1 = last_pointer_frame ();
3463 if (_drags->current_pointer_y() < grab_y()) {
3464 y1 = _drags->current_pointer_y();
3467 y2 = _drags->current_pointer_y();
3471 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3475 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3477 if (movement_occurred) {
3479 motion (event, false);
3480 do_select_things (event, false);
3486 bool do_deselect = true;
3487 MidiTimeAxisView* mtv;
3489 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3491 if (_editor->selection->empty()) {
3492 /* nothing selected */
3493 add_midi_region (mtv);
3494 do_deselect = false;
3498 /* do not deselect if Primary or Tertiary (toggle-select or
3499 * extend-select are pressed.
3502 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3503 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3510 _editor->rubberband_rect->hide();
3514 RubberbandSelectDrag::aborted (bool)
3516 _editor->rubberband_rect->hide ();
3519 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3520 : RegionDrag (e, i, p, v)
3522 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3526 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3528 Drag::start_grab (event, cursor);
3530 show_verbose_cursor_time (adjusted_current_frame (event));
3534 TimeFXDrag::motion (GdkEvent* event, bool)
3536 RegionView* rv = _primary;
3537 StreamView* cv = rv->get_time_axis_view().view ();
3539 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3540 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3541 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3543 framepos_t const pf = adjusted_current_frame (event);
3545 if (pf > rv->region()->position()) {
3546 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3549 show_verbose_cursor_time (pf);
3553 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3555 _primary->get_time_axis_view().hide_timestretch ();
3557 if (!movement_occurred) {
3561 if (last_pointer_frame() < _primary->region()->position()) {
3562 /* backwards drag of the left edge - not usable */
3566 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3568 float percentage = (double) newlen / (double) _primary->region()->length();
3570 #ifndef USE_RUBBERBAND
3571 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3572 if (_primary->region()->data_type() == DataType::AUDIO) {
3573 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3577 if (!_editor->get_selection().regions.empty()) {
3578 /* primary will already be included in the selection, and edit
3579 group shared editing will propagate selection across
3580 equivalent regions, so just use the current region
3584 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3585 error << _("An error occurred while executing time stretch operation") << endmsg;
3591 TimeFXDrag::aborted (bool)
3593 _primary->get_time_axis_view().hide_timestretch ();
3596 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3599 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3603 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3605 Drag::start_grab (event);
3609 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3611 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3615 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3617 if (movement_occurred && _editor->session()) {
3618 /* make sure we stop */
3619 _editor->session()->request_transport_speed (0.0);
3624 ScrubDrag::aborted (bool)
3629 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3634 , _original_pointer_time_axis (-1)
3635 , _last_pointer_time_axis (-1)
3636 , _time_selection_at_start (!_editor->get_selection().time.empty())
3638 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3640 if (_time_selection_at_start) {
3641 start_at_start = _editor->get_selection().time.start();
3642 end_at_start = _editor->get_selection().time.end_frame();
3647 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3649 if (_editor->session() == 0) {
3653 Gdk::Cursor* cursor = 0;
3655 switch (_operation) {
3656 case CreateSelection:
3657 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3662 cursor = _editor->cursors()->selector;
3663 Drag::start_grab (event, cursor);
3666 case SelectionStartTrim:
3667 if (_editor->clicked_axisview) {
3668 _editor->clicked_axisview->order_selection_trims (_item, true);
3670 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3673 case SelectionEndTrim:
3674 if (_editor->clicked_axisview) {
3675 _editor->clicked_axisview->order_selection_trims (_item, false);
3677 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3681 Drag::start_grab (event, cursor);
3684 case SelectionExtend:
3685 Drag::start_grab (event, cursor);
3689 if (_operation == SelectionMove) {
3690 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3692 show_verbose_cursor_time (adjusted_current_frame (event));
3695 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3699 SelectionDrag::setup_pointer_frame_offset ()
3701 switch (_operation) {
3702 case CreateSelection:
3703 _pointer_frame_offset = 0;
3706 case SelectionStartTrim:
3708 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3711 case SelectionEndTrim:
3712 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3715 case SelectionExtend:
3721 SelectionDrag::motion (GdkEvent* event, bool first_move)
3723 framepos_t start = 0;
3725 framecnt_t length = 0;
3726 framecnt_t distance = 0;
3728 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3729 if (pending_time_axis.first == 0) {
3733 framepos_t const pending_position = adjusted_current_frame (event);
3735 /* only alter selection if things have changed */
3737 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3741 switch (_operation) {
3742 case CreateSelection:
3744 framepos_t grab = grab_frame ();
3747 grab = adjusted_current_frame (event, false);
3748 if (grab < pending_position) {
3749 _editor->snap_to (grab, -1);
3751 _editor->snap_to (grab, 1);
3755 if (pending_position < grab) {
3756 start = pending_position;
3759 end = pending_position;
3763 /* first drag: Either add to the selection
3764 or create a new selection
3770 /* adding to the selection */
3771 _editor->set_selected_track_as_side_effect (Selection::Add);
3772 //_editor->selection->add (_editor->clicked_axisview);
3773 _editor->clicked_selection = _editor->selection->add (start, end);
3778 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3779 //_editor->selection->set (_editor->clicked_axisview);
3780 _editor->set_selected_track_as_side_effect (Selection::Set);
3783 _editor->clicked_selection = _editor->selection->set (start, end);
3787 /* select the track that we're in */
3788 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3789 // _editor->set_selected_track_as_side_effect (Selection::Add);
3790 _editor->selection->add (pending_time_axis.first);
3791 _added_time_axes.push_back (pending_time_axis.first);
3794 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3795 tracks that we selected in the first place.
3798 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3799 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3801 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3802 while (i != _added_time_axes.end()) {
3804 list<TimeAxisView*>::iterator tmp = i;
3807 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3808 _editor->selection->remove (*i);
3809 _added_time_axes.remove (*i);
3818 case SelectionStartTrim:
3820 start = _editor->selection->time[_editor->clicked_selection].start;
3821 end = _editor->selection->time[_editor->clicked_selection].end;
3823 if (pending_position > end) {
3826 start = pending_position;
3830 case SelectionEndTrim:
3832 start = _editor->selection->time[_editor->clicked_selection].start;
3833 end = _editor->selection->time[_editor->clicked_selection].end;
3835 if (pending_position < start) {
3838 end = pending_position;
3845 start = _editor->selection->time[_editor->clicked_selection].start;
3846 end = _editor->selection->time[_editor->clicked_selection].end;
3848 length = end - start;
3849 distance = pending_position - start;
3850 start = pending_position;
3851 _editor->snap_to (start);
3853 end = start + length;
3857 case SelectionExtend:
3861 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
3862 _editor->start_canvas_autoscroll (1, 0);
3866 switch (_operation) {
3868 if (_time_selection_at_start) {
3869 _editor->selection->move_time (distance);
3873 _editor->selection->replace (_editor->clicked_selection, start, end);
3877 if (_operation == SelectionMove) {
3878 show_verbose_cursor_time(start);
3880 show_verbose_cursor_time(pending_position);
3885 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3887 Session* s = _editor->session();
3889 if (movement_occurred) {
3890 motion (event, false);
3891 /* XXX this is not object-oriented programming at all. ick */
3892 if (_editor->selection->time.consolidate()) {
3893 _editor->selection->TimeChanged ();
3896 /* XXX what if its a music time selection? */
3898 if ( s->get_play_range() && s->transport_rolling() ) {
3899 s->request_play_range (&_editor->selection->time, true);
3901 if (Config->get_always_play_range() && !s->transport_rolling()) {
3902 s->request_locate (_editor->get_selection().time.start());
3908 /* just a click, no pointer movement.
3911 if (_operation == SelectionExtend) {
3912 if (_time_selection_at_start) {
3913 framepos_t pos = adjusted_current_frame (event, false);
3914 framepos_t start = min (pos, start_at_start);
3915 framepos_t end = max (pos, end_at_start);
3916 _editor->selection->set (start, end);
3919 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3920 if (_editor->clicked_selection) {
3921 _editor->selection->remove (_editor->clicked_selection);
3924 if (!_editor->clicked_selection) {
3925 _editor->selection->clear_time();
3930 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3931 _editor->selection->set (_editor->clicked_axisview);
3934 if (s && s->get_play_range () && s->transport_rolling()) {
3935 s->request_stop (false, false);
3940 _editor->stop_canvas_autoscroll ();
3941 _editor->clicked_selection = 0;
3945 SelectionDrag::aborted (bool)
3950 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3955 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3957 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
3958 ArdourCanvas::Rect (0.0, 0.0, 0.0,
3959 physical_screen_height (_editor->get_window())));
3960 _drag_rect->hide ();
3962 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3963 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3967 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3969 if (_editor->session() == 0) {
3973 Gdk::Cursor* cursor = 0;
3975 if (!_editor->temp_location) {
3976 _editor->temp_location = new Location (*_editor->session());
3979 switch (_operation) {
3980 case CreateRangeMarker:
3981 case CreateTransportMarker:
3982 case CreateCDMarker:
3984 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3989 cursor = _editor->cursors()->selector;
3993 Drag::start_grab (event, cursor);
3995 show_verbose_cursor_time (adjusted_current_frame (event));
3999 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4001 framepos_t start = 0;
4003 ArdourCanvas::Rectangle *crect;
4005 switch (_operation) {
4006 case CreateRangeMarker:
4007 crect = _editor->range_bar_drag_rect;
4009 case CreateTransportMarker:
4010 crect = _editor->transport_bar_drag_rect;
4012 case CreateCDMarker:
4013 crect = _editor->cd_marker_bar_drag_rect;
4016 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4021 framepos_t const pf = adjusted_current_frame (event);
4023 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4024 framepos_t grab = grab_frame ();
4025 _editor->snap_to (grab);
4027 if (pf < grab_frame()) {
4035 /* first drag: Either add to the selection
4036 or create a new selection.
4041 _editor->temp_location->set (start, end);
4045 update_item (_editor->temp_location);
4047 //_drag_rect->raise_to_top();
4052 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
4053 _editor->start_canvas_autoscroll (1, 0);
4057 _editor->temp_location->set (start, end);
4059 double x1 = _editor->sample_to_pixel (start);
4060 double x2 = _editor->sample_to_pixel (end);
4064 update_item (_editor->temp_location);
4067 show_verbose_cursor_time (pf);
4072 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4074 Location * newloc = 0;
4078 if (movement_occurred) {
4079 motion (event, false);
4082 switch (_operation) {
4083 case CreateRangeMarker:
4084 case CreateCDMarker:
4086 _editor->begin_reversible_command (_("new range marker"));
4087 XMLNode &before = _editor->session()->locations()->get_state();
4088 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4089 if (_operation == CreateCDMarker) {
4090 flags = Location::IsRangeMarker | Location::IsCDMarker;
4091 _editor->cd_marker_bar_drag_rect->hide();
4094 flags = Location::IsRangeMarker;
4095 _editor->range_bar_drag_rect->hide();
4097 newloc = new Location (
4098 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4101 _editor->session()->locations()->add (newloc, true);
4102 XMLNode &after = _editor->session()->locations()->get_state();
4103 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4104 _editor->commit_reversible_command ();
4108 case CreateTransportMarker:
4109 // popup menu to pick loop or punch
4110 _editor->new_transport_marker_context_menu (&event->button, _item);
4114 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4116 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
4121 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4123 if (end == max_framepos) {
4124 end = _editor->session()->current_end_frame ();
4127 if (start == max_framepos) {
4128 start = _editor->session()->current_start_frame ();
4131 switch (_editor->mouse_mode) {
4133 /* find the two markers on either side and then make the selection from it */
4134 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4138 /* find the two markers on either side of the click and make the range out of it */
4139 _editor->selection->set (start, end);
4148 _editor->stop_canvas_autoscroll ();
4152 RangeMarkerBarDrag::aborted (bool)
4158 RangeMarkerBarDrag::update_item (Location* location)
4160 double const x1 = _editor->sample_to_pixel (location->start());
4161 double const x2 = _editor->sample_to_pixel (location->end());
4163 _drag_rect->set_x0 (x1);
4164 _drag_rect->set_x1 (x2);
4167 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4171 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4175 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4177 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4178 Drag::start_grab (event, _editor->cursors()->zoom_out);
4181 Drag::start_grab (event, _editor->cursors()->zoom_in);
4185 show_verbose_cursor_time (adjusted_current_frame (event));
4189 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4194 framepos_t const pf = adjusted_current_frame (event);
4196 framepos_t grab = grab_frame ();
4197 _editor->snap_to_with_modifier (grab, event);
4199 /* base start and end on initial click position */
4211 _editor->zoom_rect->show();
4212 _editor->zoom_rect->raise_to_top();
4215 _editor->reposition_zoom_rect(start, end);
4217 show_verbose_cursor_time (pf);
4222 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4224 if (movement_occurred) {
4225 motion (event, false);
4227 if (grab_frame() < last_pointer_frame()) {
4228 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4230 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4233 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4234 _editor->tav_zoom_step (_zoom_out);
4236 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4240 _editor->zoom_rect->hide();
4244 MouseZoomDrag::aborted (bool)
4246 _editor->zoom_rect->hide ();
4249 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4251 , _cumulative_dx (0)
4252 , _cumulative_dy (0)
4254 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4256 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4258 _region = &_primary->region_view ();
4259 _note_height = _region->midi_stream_view()->note_height ();
4263 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4265 Drag::start_grab (event);
4267 if (!(_was_selected = _primary->selected())) {
4269 /* tertiary-click means extend selection - we'll do that on button release,
4270 so don't add it here, because otherwise we make it hard to figure
4271 out the "extend-to" range.
4274 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4277 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4280 _region->note_selected (_primary, true);
4282 _region->unique_select (_primary);
4288 /** @return Current total drag x change in frames */
4290 NoteDrag::total_dx () const
4293 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4295 /* primary note time */
4296 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4298 /* new time of the primary note in session frames */
4299 frameoffset_t st = n + dx;
4301 framepos_t const rp = _region->region()->position ();
4303 /* prevent the note being dragged earlier than the region's position */
4306 /* snap and return corresponding delta */
4307 return _region->snap_frame_to_frame (st - rp) + rp - n;
4310 /** @return Current total drag y change in note number */
4312 NoteDrag::total_dy () const
4314 MidiStreamView* msv = _region->midi_stream_view ();
4315 double const y = _region->midi_view()->y_position ();
4316 /* new current note */
4317 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4319 n = max (msv->lowest_note(), n);
4320 n = min (msv->highest_note(), n);
4321 /* and work out delta */
4322 return n - msv->y_to_note (grab_y() - y);
4326 NoteDrag::motion (GdkEvent *, bool)
4328 /* Total change in x and y since the start of the drag */
4329 frameoffset_t const dx = total_dx ();
4330 int8_t const dy = total_dy ();
4332 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4333 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4334 double const tdy = -dy * _note_height - _cumulative_dy;
4337 _cumulative_dx += tdx;
4338 _cumulative_dy += tdy;
4340 int8_t note_delta = total_dy();
4342 _region->move_selection (tdx, tdy, note_delta);
4344 /* the new note value may be the same as the old one, but we
4345 * don't know what that means because the selection may have
4346 * involved more than one note and we might be doing something
4347 * odd with them. so show the note value anyway, always.
4351 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4353 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4354 (int) floor (new_note));
4356 show_verbose_cursor_text (buf);
4361 NoteDrag::finished (GdkEvent* ev, bool moved)
4364 /* no motion - select note */
4366 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4367 _editor->current_mouse_mode() == Editing::MouseDraw) {
4369 if (_was_selected) {
4370 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4372 _region->note_deselected (_primary);
4375 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4376 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4378 if (!extend && !add && _region->selection_size() > 1) {
4379 _region->unique_select (_primary);
4380 } else if (extend) {
4381 _region->note_selected (_primary, true, true);
4383 /* it was added during button press */
4388 _region->note_dropped (_primary, total_dx(), total_dy());
4393 NoteDrag::aborted (bool)
4398 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4399 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4400 : Drag (editor, atv->base_item ())
4402 , _nothing_to_drag (false)
4404 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4405 y_origin = atv->y_position();
4406 setup (atv->lines ());
4409 /** Make an AutomationRangeDrag for region gain lines */
4410 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4411 : Drag (editor, rv->get_canvas_group ())
4413 , _nothing_to_drag (false)
4415 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4417 list<boost::shared_ptr<AutomationLine> > lines;
4418 lines.push_back (rv->get_gain_line ());
4419 y_origin = rv->get_time_axis_view().y_position();
4423 /** @param lines AutomationLines to drag.
4424 * @param offset Offset from the session start to the points in the AutomationLines.
4427 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4429 /* find the lines that overlap the ranges being dragged */
4430 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4431 while (i != lines.end ()) {
4432 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4435 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4437 /* check this range against all the AudioRanges that we are using */
4438 list<AudioRange>::const_iterator k = _ranges.begin ();
4439 while (k != _ranges.end()) {
4440 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4446 /* add it to our list if it overlaps at all */
4447 if (k != _ranges.end()) {
4452 _lines.push_back (n);
4458 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4462 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4464 return 1.0 - ((global_y - y_origin) / line->height());
4468 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4470 Drag::start_grab (event, cursor);
4472 /* Get line states before we start changing things */
4473 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4474 i->state = &i->line->get_state ();
4475 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4478 if (_ranges.empty()) {
4480 /* No selected time ranges: drag all points */
4481 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4482 uint32_t const N = i->line->npoints ();
4483 for (uint32_t j = 0; j < N; ++j) {
4484 i->points.push_back (i->line->nth (j));
4490 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4492 framecnt_t const half = (i->start + i->end) / 2;
4494 /* find the line that this audio range starts in */
4495 list<Line>::iterator j = _lines.begin();
4496 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4500 if (j != _lines.end()) {
4501 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4503 /* j is the line that this audio range starts in; fade into it;
4504 64 samples length plucked out of thin air.
4507 framepos_t a = i->start + 64;
4512 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4513 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4515 the_list->add (p, the_list->eval (p));
4516 the_list->add (q, the_list->eval (q));
4519 /* same thing for the end */
4522 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4526 if (j != _lines.end()) {
4527 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4529 /* j is the line that this audio range starts in; fade out of it;
4530 64 samples length plucked out of thin air.
4533 framepos_t b = i->end - 64;
4538 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4539 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4541 the_list->add (p, the_list->eval (p));
4542 the_list->add (q, the_list->eval (q));
4546 _nothing_to_drag = true;
4548 /* Find all the points that should be dragged and put them in the relevant
4549 points lists in the Line structs.
4552 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4554 uint32_t const N = i->line->npoints ();
4555 for (uint32_t j = 0; j < N; ++j) {
4557 /* here's a control point on this line */
4558 ControlPoint* p = i->line->nth (j);
4559 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4561 /* see if it's inside a range */
4562 list<AudioRange>::const_iterator k = _ranges.begin ();
4563 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4567 if (k != _ranges.end()) {
4568 /* dragging this point */
4569 _nothing_to_drag = false;
4570 i->points.push_back (p);
4576 if (_nothing_to_drag) {
4580 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4581 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4586 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4588 if (_nothing_to_drag) {
4592 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4593 float const f = y_fraction (l->line, _drags->current_pointer_y());
4594 /* we are ignoring x position for this drag, so we can just pass in anything */
4596 l->line->drag_motion (0, f, true, false, ignored);
4597 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4602 AutomationRangeDrag::finished (GdkEvent* event, bool)
4604 if (_nothing_to_drag) {
4608 motion (event, false);
4609 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4610 i->line->end_drag (false, 0);
4613 _editor->session()->commit_reversible_command ();
4617 AutomationRangeDrag::aborted (bool)
4619 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4624 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4627 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4628 layer = v->region()->layer ();
4629 initial_y = v->get_canvas_group()->position().y;
4630 initial_playlist = v->region()->playlist ();
4631 initial_position = v->region()->position ();
4632 initial_end = v->region()->position () + v->region()->length ();
4635 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4636 : Drag (e, i->canvas_item ())
4639 , _cumulative_dx (0)
4641 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4642 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4647 PatchChangeDrag::motion (GdkEvent* ev, bool)
4649 framepos_t f = adjusted_current_frame (ev);
4650 boost::shared_ptr<Region> r = _region_view->region ();
4651 f = max (f, r->position ());
4652 f = min (f, r->last_frame ());
4654 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4655 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4656 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4657 _cumulative_dx = dxu;
4661 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4663 if (!movement_occurred) {
4667 boost::shared_ptr<Region> r (_region_view->region ());
4668 framepos_t f = adjusted_current_frame (ev);
4669 f = max (f, r->position ());
4670 f = min (f, r->last_frame ());
4672 _region_view->move_patch_change (
4674 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4679 PatchChangeDrag::aborted (bool)
4681 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4685 PatchChangeDrag::setup_pointer_frame_offset ()
4687 boost::shared_ptr<Region> region = _region_view->region ();
4688 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4691 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4692 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4699 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4701 framepos_t const p = _region_view->region()->position ();
4702 double const y = _region_view->midi_view()->y_position ();
4704 x1 = max ((framepos_t) 0, x1 - p);
4705 x2 = max ((framepos_t) 0, x2 - p);
4706 y1 = max (0.0, y1 - y);
4707 y2 = max (0.0, y2 - y);
4709 _region_view->update_drag_selection (
4710 _editor->sample_to_pixel (x1),
4711 _editor->sample_to_pixel (x2),
4714 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4719 MidiRubberbandSelectDrag::deselect_things ()
4724 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4725 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4728 _vertical_only = true;
4732 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4734 double const y = _region_view->midi_view()->y_position ();
4736 y1 = max (0.0, y1 - y);
4737 y2 = max (0.0, y2 - y);
4739 _region_view->update_vertical_drag_selection (
4742 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4747 MidiVerticalSelectDrag::deselect_things ()
4752 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4753 : RubberbandSelectDrag (e, i)
4759 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4761 if (drag_in_progress) {
4762 /* We just want to select things at the end of the drag, not during it */
4766 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4768 _editor->begin_reversible_command (_("rubberband selection"));
4769 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4770 _editor->commit_reversible_command ();
4774 EditorRubberbandSelectDrag::deselect_things ()
4776 if (!getenv("ARDOUR_SAE")) {
4777 _editor->selection->clear_tracks();
4779 _editor->selection->clear_regions();
4780 _editor->selection->clear_points ();
4781 _editor->selection->clear_lines ();
4784 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4792 NoteCreateDrag::~NoteCreateDrag ()
4798 NoteCreateDrag::grid_frames (framepos_t t) const
4801 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4806 return _region_view->region_beats_to_region_frames (grid_beats);
4810 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4812 Drag::start_grab (event, cursor);
4814 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4816 framepos_t pf = _drags->current_pointer_frame ();
4817 framecnt_t const g = grid_frames (pf);
4819 /* Hack so that we always snap to the note that we are over, instead of snapping
4820 to the next one if we're more than halfway through the one we're over.
4822 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4826 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4828 MidiStreamView* sv = _region_view->midi_stream_view ();
4829 double const x = _editor->sample_to_pixel (_note[0]);
4830 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4832 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4833 _drag_rect->set_outline_what (0xff);
4834 _drag_rect->set_outline_color (0xffffff99);
4835 _drag_rect->set_fill_color (0xffffff66);
4839 NoteCreateDrag::motion (GdkEvent* event, bool)
4841 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4842 double const x = _editor->sample_to_pixel (_note[1]);
4843 if (_note[1] > _note[0]) {
4844 _drag_rect->set_x1 (x);
4846 _drag_rect->set_x0 (x);
4851 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4853 if (!had_movement) {
4857 framepos_t const start = min (_note[0], _note[1]);
4858 framecnt_t length = (framecnt_t) fabs (_note[0] - _note[1]);
4860 framecnt_t const g = grid_frames (start);
4861 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4863 if (_editor->snap_mode() == SnapNormal && length < g) {
4864 length = g - one_tick;
4867 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4869 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
4873 NoteCreateDrag::y_to_region (double y) const
4876 _region_view->get_canvas_group()->canvas_to_item (x, y);
4881 NoteCreateDrag::aborted (bool)
4886 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4891 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
4895 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4897 Drag::start_grab (event, cursor);
4901 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4907 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4910 distance = _drags->current_pointer_x() - grab_x();
4911 len = ar->fade_in()->back()->when;
4913 distance = grab_x() - _drags->current_pointer_x();
4914 len = ar->fade_out()->back()->when;
4917 /* how long should it be ? */
4919 new_length = len + _editor->pixel_to_sample (distance);
4921 /* now check with the region that this is legal */
4923 new_length = ar->verify_xfade_bounds (new_length, start);
4926 arv->reset_fade_in_shape_width (ar, new_length);
4928 arv->reset_fade_out_shape_width (ar, new_length);
4933 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
4939 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4942 distance = _drags->current_pointer_x() - grab_x();
4943 len = ar->fade_in()->back()->when;
4945 distance = grab_x() - _drags->current_pointer_x();
4946 len = ar->fade_out()->back()->when;
4949 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
4951 _editor->begin_reversible_command ("xfade trim");
4952 ar->playlist()->clear_owned_changes ();
4955 ar->set_fade_in_length (new_length);
4957 ar->set_fade_out_length (new_length);
4960 /* Adjusting the xfade may affect other regions in the playlist, so we need
4961 to get undo Commands from the whole playlist rather than just the
4965 vector<Command*> cmds;
4966 ar->playlist()->rdiff (cmds);
4967 _editor->session()->add_commands (cmds);
4968 _editor->commit_reversible_command ();
4973 CrossfadeEdgeDrag::aborted (bool)
4976 arv->redraw_start_xfade ();
4978 arv->redraw_end_xfade ();