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->canvas_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 () < rb->route()->order_key ();
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 ((double)(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(_startdrag_video_offset+dt) - _startdrag_video_offset;
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 /* This is asynchronous so it will be sent "now"
2382 s->send_mmc_locate (f);
2383 /* These are synchronous and will be sent during the next
2386 s->queue_full_time_code ();
2387 s->queue_song_position_pointer ();
2390 show_verbose_cursor_time (t);
2391 _editor->UpdateAllTransportClocks (t);
2395 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2397 Drag::start_grab (event, c);
2399 _grab_zoom = _editor->samples_per_pixel;
2401 framepos_t where = _editor->canvas_event_frame (event);
2403 _editor->snap_to_with_modifier (where, event);
2405 _editor->_dragging_playhead = true;
2407 Session* s = _editor->session ();
2409 /* grab the track canvas item as well */
2411 _cursor.track_canvas_item().grab();
2414 if (_was_rolling && _stop) {
2418 if (s->is_auditioning()) {
2419 s->cancel_audition ();
2423 if (AudioEngine::instance()->connected()) {
2425 /* do this only if we're the engine is connected
2426 * because otherwise this request will never be
2427 * serviced and we'll busy wait forever. likewise,
2428 * notice if we are disconnected while waiting for the
2429 * request to be serviced.
2432 s->request_suspend_timecode_transmission ();
2433 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2434 /* twiddle our thumbs */
2439 fake_locate (where);
2443 CursorDrag::motion (GdkEvent* event, bool)
2445 framepos_t const adjusted_frame = adjusted_current_frame (event);
2446 if (adjusted_frame != last_pointer_frame()) {
2447 fake_locate (adjusted_frame);
2452 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2454 _editor->_dragging_playhead = false;
2456 _cursor.track_canvas_item().ungrab();
2458 if (!movement_occurred && _stop) {
2462 motion (event, false);
2464 Session* s = _editor->session ();
2466 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2467 _editor->_pending_locate_request = true;
2468 s->request_resume_timecode_transmission ();
2473 CursorDrag::aborted (bool)
2475 _cursor.track_canvas_item().ungrab();
2477 if (_editor->_dragging_playhead) {
2478 _editor->session()->request_resume_timecode_transmission ();
2479 _editor->_dragging_playhead = false;
2482 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2485 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2486 : RegionDrag (e, i, p, v)
2488 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2492 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2494 Drag::start_grab (event, cursor);
2496 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2497 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2499 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2503 FadeInDrag::setup_pointer_frame_offset ()
2505 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2506 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2507 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2511 FadeInDrag::motion (GdkEvent* event, bool)
2513 framecnt_t fade_length;
2515 framepos_t const pos = adjusted_current_frame (event);
2517 boost::shared_ptr<Region> region = _primary->region ();
2519 if (pos < (region->position() + 64)) {
2520 fade_length = 64; // this should be a minimum defined somewhere
2521 } else if (pos > region->last_frame()) {
2522 fade_length = region->length();
2524 fade_length = pos - region->position();
2527 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2529 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2535 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2538 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2542 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2544 if (!movement_occurred) {
2548 framecnt_t fade_length;
2550 framepos_t const pos = adjusted_current_frame (event);
2552 boost::shared_ptr<Region> region = _primary->region ();
2554 if (pos < (region->position() + 64)) {
2555 fade_length = 64; // this should be a minimum defined somewhere
2556 } else if (pos > region->last_frame()) {
2557 fade_length = region->length();
2559 fade_length = pos - region->position();
2562 _editor->begin_reversible_command (_("change fade in length"));
2564 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2566 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2572 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2573 XMLNode &before = alist->get_state();
2575 tmp->audio_region()->set_fade_in_length (fade_length);
2576 tmp->audio_region()->set_fade_in_active (true);
2578 XMLNode &after = alist->get_state();
2579 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2582 _editor->commit_reversible_command ();
2586 FadeInDrag::aborted (bool)
2588 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2589 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2595 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2599 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2600 : RegionDrag (e, i, p, v)
2602 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2606 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2608 Drag::start_grab (event, cursor);
2610 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2611 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2613 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2617 FadeOutDrag::setup_pointer_frame_offset ()
2619 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2620 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2621 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2625 FadeOutDrag::motion (GdkEvent* event, bool)
2627 framecnt_t fade_length;
2629 framepos_t const pos = adjusted_current_frame (event);
2631 boost::shared_ptr<Region> region = _primary->region ();
2633 if (pos > (region->last_frame() - 64)) {
2634 fade_length = 64; // this should really be a minimum fade defined somewhere
2636 else if (pos < region->position()) {
2637 fade_length = region->length();
2640 fade_length = region->last_frame() - pos;
2643 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2645 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2651 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2654 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2658 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2660 if (!movement_occurred) {
2664 framecnt_t fade_length;
2666 framepos_t const pos = adjusted_current_frame (event);
2668 boost::shared_ptr<Region> region = _primary->region ();
2670 if (pos > (region->last_frame() - 64)) {
2671 fade_length = 64; // this should really be a minimum fade defined somewhere
2673 else if (pos < region->position()) {
2674 fade_length = region->length();
2677 fade_length = region->last_frame() - pos;
2680 _editor->begin_reversible_command (_("change fade out length"));
2682 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2684 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2690 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2691 XMLNode &before = alist->get_state();
2693 tmp->audio_region()->set_fade_out_length (fade_length);
2694 tmp->audio_region()->set_fade_out_active (true);
2696 XMLNode &after = alist->get_state();
2697 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2700 _editor->commit_reversible_command ();
2704 FadeOutDrag::aborted (bool)
2706 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2707 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2713 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2717 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2720 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2722 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2725 _points.push_back (ArdourCanvas::Duple (0, 0));
2726 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2729 MarkerDrag::~MarkerDrag ()
2731 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2736 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2738 location = new Location (*l);
2739 markers.push_back (m);
2744 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2746 Drag::start_grab (event, cursor);
2750 Location *location = _editor->find_location_from_marker (_marker, is_start);
2751 _editor->_dragging_edit_point = true;
2753 update_item (location);
2755 // _drag_line->show();
2756 // _line->raise_to_top();
2759 show_verbose_cursor_time (location->start());
2761 show_verbose_cursor_time (location->end());
2764 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2767 case Selection::Toggle:
2768 /* we toggle on the button release */
2770 case Selection::Set:
2771 if (!_editor->selection->selected (_marker)) {
2772 _editor->selection->set (_marker);
2775 case Selection::Extend:
2777 Locations::LocationList ll;
2778 list<Marker*> to_add;
2780 _editor->selection->markers.range (s, e);
2781 s = min (_marker->position(), s);
2782 e = max (_marker->position(), e);
2785 if (e < max_framepos) {
2788 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2789 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2790 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2793 to_add.push_back (lm->start);
2796 to_add.push_back (lm->end);
2800 if (!to_add.empty()) {
2801 _editor->selection->add (to_add);
2805 case Selection::Add:
2806 _editor->selection->add (_marker);
2810 /* Set up copies for us to manipulate during the drag
2813 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2815 Location* l = _editor->find_location_from_marker (*i, is_start);
2822 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2824 /* range: check that the other end of the range isn't
2827 CopiedLocationInfo::iterator x;
2828 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2829 if (*(*x).location == *l) {
2833 if (x == _copied_locations.end()) {
2834 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2836 (*x).markers.push_back (*i);
2837 (*x).move_both = true;
2845 MarkerDrag::setup_pointer_frame_offset ()
2848 Location *location = _editor->find_location_from_marker (_marker, is_start);
2849 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2853 MarkerDrag::motion (GdkEvent* event, bool)
2855 framecnt_t f_delta = 0;
2857 bool move_both = false;
2858 Location *real_location;
2859 Location *copy_location = 0;
2861 framepos_t const newframe = adjusted_current_frame (event);
2862 framepos_t next = newframe;
2864 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2868 CopiedLocationInfo::iterator x;
2870 /* find the marker we're dragging, and compute the delta */
2872 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2874 copy_location = (*x).location;
2876 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2878 /* this marker is represented by this
2879 * CopiedLocationMarkerInfo
2882 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
2887 if (real_location->is_mark()) {
2888 f_delta = newframe - copy_location->start();
2892 switch (_marker->type()) {
2893 case Marker::SessionStart:
2894 case Marker::RangeStart:
2895 case Marker::LoopStart:
2896 case Marker::PunchIn:
2897 f_delta = newframe - copy_location->start();
2900 case Marker::SessionEnd:
2901 case Marker::RangeEnd:
2902 case Marker::LoopEnd:
2903 case Marker::PunchOut:
2904 f_delta = newframe - copy_location->end();
2907 /* what kind of marker is this ? */
2916 if (x == _copied_locations.end()) {
2917 /* hmm, impossible - we didn't find the dragged marker */
2921 /* now move them all */
2923 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2925 copy_location = x->location;
2927 /* call this to find out if its the start or end */
2929 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
2933 if (real_location->locked()) {
2937 if (copy_location->is_mark()) {
2941 copy_location->set_start (copy_location->start() + f_delta);
2945 framepos_t new_start = copy_location->start() + f_delta;
2946 framepos_t new_end = copy_location->end() + f_delta;
2948 if (is_start) { // start-of-range marker
2950 if (move_both || (*x).move_both) {
2951 copy_location->set_start (new_start);
2952 copy_location->set_end (new_end);
2953 } else if (new_start < copy_location->end()) {
2954 copy_location->set_start (new_start);
2955 } else if (newframe > 0) {
2956 _editor->snap_to (next, 1, true);
2957 copy_location->set_end (next);
2958 copy_location->set_start (newframe);
2961 } else { // end marker
2963 if (move_both || (*x).move_both) {
2964 copy_location->set_end (new_end);
2965 copy_location->set_start (new_start);
2966 } else if (new_end > copy_location->start()) {
2967 copy_location->set_end (new_end);
2968 } else if (newframe > 0) {
2969 _editor->snap_to (next, -1, true);
2970 copy_location->set_start (next);
2971 copy_location->set_end (newframe);
2976 update_item (copy_location);
2978 /* now lookup the actual GUI items used to display this
2979 * location and move them to wherever the copy of the location
2980 * is now. This means that the logic in ARDOUR::Location is
2981 * still enforced, even though we are not (yet) modifying
2982 * the real Location itself.
2985 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2988 lm->set_position (copy_location->start(), copy_location->end());
2993 assert (!_copied_locations.empty());
2995 show_verbose_cursor_time (newframe);
2999 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3001 if (!movement_occurred) {
3003 if (was_double_click()) {
3004 cerr << "End of marker double click\n";
3007 /* just a click, do nothing but finish
3008 off the selection process
3011 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3014 case Selection::Set:
3015 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3016 _editor->selection->set (_marker);
3020 case Selection::Toggle:
3021 /* we toggle on the button release, click only */
3022 _editor->selection->toggle (_marker);
3025 case Selection::Extend:
3026 case Selection::Add:
3033 _editor->_dragging_edit_point = false;
3035 _editor->begin_reversible_command ( _("move marker") );
3036 XMLNode &before = _editor->session()->locations()->get_state();
3038 MarkerSelection::iterator i;
3039 CopiedLocationInfo::iterator x;
3042 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3043 x != _copied_locations.end() && i != _editor->selection->markers.end();
3046 Location * location = _editor->find_location_from_marker (*i, is_start);
3050 if (location->locked()) {
3054 if (location->is_mark()) {
3055 location->set_start (((*x).location)->start());
3057 location->set (((*x).location)->start(), ((*x).location)->end());
3062 XMLNode &after = _editor->session()->locations()->get_state();
3063 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3064 _editor->commit_reversible_command ();
3068 MarkerDrag::aborted (bool)
3074 MarkerDrag::update_item (Location*)
3079 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3081 _cumulative_x_drag (0),
3082 _cumulative_y_drag (0)
3084 if (_zero_gain_fraction < 0.0) {
3085 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3088 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3090 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3096 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3098 Drag::start_grab (event, _editor->cursors()->fader);
3100 // start the grab at the center of the control point so
3101 // the point doesn't 'jump' to the mouse after the first drag
3102 _fixed_grab_x = _point->get_x();
3103 _fixed_grab_y = _point->get_y();
3105 float const fraction = 1 - (_point->get_y() / _point->line().height());
3107 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3109 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3110 event->button.x + 10, event->button.y + 10);
3112 _editor->verbose_cursor()->show ();
3114 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3116 if (!_point->can_slide ()) {
3117 _x_constrained = true;
3122 ControlPointDrag::motion (GdkEvent* event, bool)
3124 double dx = _drags->current_pointer_x() - last_pointer_x();
3125 double dy = _drags->current_pointer_y() - last_pointer_y();
3127 if (event->button.state & Keyboard::SecondaryModifier) {
3132 /* coordinate in pixels relative to the start of the region (for region-based automation)
3133 or track (for track-based automation) */
3134 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3135 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3137 // calculate zero crossing point. back off by .01 to stay on the
3138 // positive side of zero
3139 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3141 // make sure we hit zero when passing through
3142 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3146 if (_x_constrained) {
3149 if (_y_constrained) {
3153 _cumulative_x_drag = cx - _fixed_grab_x;
3154 _cumulative_y_drag = cy - _fixed_grab_y;
3158 cy = min ((double) _point->line().height(), cy);
3160 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3162 if (!_x_constrained) {
3163 _editor->snap_to_with_modifier (cx_frames, event);
3166 cx_frames = min (cx_frames, _point->line().maximum_time());
3168 float const fraction = 1.0 - (cy / _point->line().height());
3170 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3172 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3176 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3178 if (!movement_occurred) {
3182 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3183 _editor->reset_point_selection ();
3187 motion (event, false);
3190 _point->line().end_drag (_pushing, _final_index);
3191 _editor->session()->commit_reversible_command ();
3195 ControlPointDrag::aborted (bool)
3197 _point->line().reset ();
3201 ControlPointDrag::active (Editing::MouseMode m)
3203 if (m == Editing::MouseGain) {
3204 /* always active in mouse gain */
3208 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3209 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3212 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3215 _cumulative_y_drag (0)
3217 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3221 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3223 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3226 _item = &_line->grab_item ();
3228 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3229 origin, and ditto for y.
3232 double cx = event->button.x;
3233 double cy = event->button.y;
3235 _line->parent_group().canvas_to_item (cx, cy);
3237 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3242 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3243 /* no adjacent points */
3247 Drag::start_grab (event, _editor->cursors()->fader);
3249 /* store grab start in parent frame */
3254 double fraction = 1.0 - (cy / _line->height());
3256 _line->start_drag_line (before, after, fraction);
3258 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3259 event->button.x + 10, event->button.y + 10);
3261 _editor->verbose_cursor()->show ();
3265 LineDrag::motion (GdkEvent* event, bool)
3267 double dy = _drags->current_pointer_y() - last_pointer_y();
3269 if (event->button.state & Keyboard::SecondaryModifier) {
3273 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3275 _cumulative_y_drag = cy - _fixed_grab_y;
3278 cy = min ((double) _line->height(), cy);
3280 double const fraction = 1.0 - (cy / _line->height());
3283 /* we are ignoring x position for this drag, so we can just pass in anything */
3284 _line->drag_motion (0, fraction, true, false, ignored);
3286 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3290 LineDrag::finished (GdkEvent* event, bool)
3292 motion (event, false);
3293 _line->end_drag (false, 0);
3294 _editor->session()->commit_reversible_command ();
3298 LineDrag::aborted (bool)
3303 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3306 _cumulative_x_drag (0)
3308 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3312 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3314 Drag::start_grab (event);
3316 _line = reinterpret_cast<Line*> (_item);
3319 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3321 double cx = event->button.x;
3322 double cy = event->button.y;
3324 _item->parent()->canvas_to_item (cx, cy);
3326 /* store grab start in parent frame */
3327 _region_view_grab_x = cx;
3329 _before = *(float*) _item->get_data ("position");
3331 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3333 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3337 FeatureLineDrag::motion (GdkEvent*, bool)
3339 double dx = _drags->current_pointer_x() - last_pointer_x();
3341 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3343 _cumulative_x_drag += dx;
3345 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3354 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3356 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3358 float *pos = new float;
3361 _line->set_data ("position", pos);
3367 FeatureLineDrag::finished (GdkEvent*, bool)
3369 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3370 _arv->update_transient(_before, _before);
3374 FeatureLineDrag::aborted (bool)
3379 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3381 , _vertical_only (false)
3383 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3387 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3389 Drag::start_grab (event);
3390 show_verbose_cursor_time (adjusted_current_frame (event));
3394 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3401 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3403 framepos_t grab = grab_frame ();
3404 if (Config->get_rubberbanding_snaps_to_grid ()) {
3405 _editor->snap_to_with_modifier (grab, event);
3408 /* base start and end on initial click position */
3418 if (_drags->current_pointer_y() < grab_y()) {
3419 y1 = _drags->current_pointer_y();
3422 y2 = _drags->current_pointer_y();
3427 if (start != end || y1 != y2) {
3429 double x1 = _editor->sample_to_pixel (start);
3430 double x2 = _editor->sample_to_pixel (end);
3431 const double min_dimension = 2.0;
3433 _editor->rubberband_rect->set_x0 (x1);
3434 if (_vertical_only) {
3435 /* fixed 10 pixel width */
3436 _editor->rubberband_rect->set_x1 (x1 + 10);
3439 x2 = min (x1 - min_dimension, x2);
3441 x2 = max (x1 + min_dimension, x2);
3443 _editor->rubberband_rect->set_x1 (x2);
3446 _editor->rubberband_rect->set_y0 (y1);
3448 y2 = min (y1 - min_dimension, y2);
3450 y2 = max (y1 + min_dimension, y2);
3453 _editor->rubberband_rect->set_y1 (y2);
3455 _editor->rubberband_rect->show();
3456 _editor->rubberband_rect->raise_to_top();
3458 show_verbose_cursor_time (pf);
3460 do_select_things (event, true);
3465 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3470 if (grab_frame() < last_pointer_frame()) {
3472 x2 = last_pointer_frame ();
3475 x1 = last_pointer_frame ();
3481 if (_drags->current_pointer_y() < grab_y()) {
3482 y1 = _drags->current_pointer_y();
3485 y2 = _drags->current_pointer_y();
3489 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3493 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3495 if (movement_occurred) {
3497 motion (event, false);
3498 do_select_things (event, false);
3504 bool do_deselect = true;
3505 MidiTimeAxisView* mtv;
3507 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3509 if (_editor->selection->empty()) {
3510 /* nothing selected */
3511 add_midi_region (mtv);
3512 do_deselect = false;
3516 /* do not deselect if Primary or Tertiary (toggle-select or
3517 * extend-select are pressed.
3520 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3521 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3528 _editor->rubberband_rect->hide();
3532 RubberbandSelectDrag::aborted (bool)
3534 _editor->rubberband_rect->hide ();
3537 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3538 : RegionDrag (e, i, p, v)
3540 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3544 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3546 Drag::start_grab (event, cursor);
3548 show_verbose_cursor_time (adjusted_current_frame (event));
3552 TimeFXDrag::motion (GdkEvent* event, bool)
3554 RegionView* rv = _primary;
3555 StreamView* cv = rv->get_time_axis_view().view ();
3557 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3558 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3559 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3561 framepos_t const pf = adjusted_current_frame (event);
3563 if (pf > rv->region()->position()) {
3564 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3567 show_verbose_cursor_time (pf);
3571 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3573 _primary->get_time_axis_view().hide_timestretch ();
3575 if (!movement_occurred) {
3579 if (last_pointer_frame() < _primary->region()->position()) {
3580 /* backwards drag of the left edge - not usable */
3584 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3586 float percentage = (double) newlen / (double) _primary->region()->length();
3588 #ifndef USE_RUBBERBAND
3589 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3590 if (_primary->region()->data_type() == DataType::AUDIO) {
3591 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3595 if (!_editor->get_selection().regions.empty()) {
3596 /* primary will already be included in the selection, and edit
3597 group shared editing will propagate selection across
3598 equivalent regions, so just use the current region
3602 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3603 error << _("An error occurred while executing time stretch operation") << endmsg;
3609 TimeFXDrag::aborted (bool)
3611 _primary->get_time_axis_view().hide_timestretch ();
3614 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3617 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3621 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3623 Drag::start_grab (event);
3627 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3629 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3633 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3635 if (movement_occurred && _editor->session()) {
3636 /* make sure we stop */
3637 _editor->session()->request_transport_speed (0.0);
3642 ScrubDrag::aborted (bool)
3647 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3652 , _original_pointer_time_axis (-1)
3653 , _last_pointer_time_axis (-1)
3654 , _time_selection_at_start (!_editor->get_selection().time.empty())
3656 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3658 if (_time_selection_at_start) {
3659 start_at_start = _editor->get_selection().time.start();
3660 end_at_start = _editor->get_selection().time.end_frame();
3665 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3667 if (_editor->session() == 0) {
3671 Gdk::Cursor* cursor = 0;
3673 switch (_operation) {
3674 case CreateSelection:
3675 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3680 cursor = _editor->cursors()->selector;
3681 Drag::start_grab (event, cursor);
3684 case SelectionStartTrim:
3685 if (_editor->clicked_axisview) {
3686 _editor->clicked_axisview->order_selection_trims (_item, true);
3688 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3691 case SelectionEndTrim:
3692 if (_editor->clicked_axisview) {
3693 _editor->clicked_axisview->order_selection_trims (_item, false);
3695 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3699 Drag::start_grab (event, cursor);
3702 case SelectionExtend:
3703 Drag::start_grab (event, cursor);
3707 if (_operation == SelectionMove) {
3708 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3710 show_verbose_cursor_time (adjusted_current_frame (event));
3713 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3717 SelectionDrag::setup_pointer_frame_offset ()
3719 switch (_operation) {
3720 case CreateSelection:
3721 _pointer_frame_offset = 0;
3724 case SelectionStartTrim:
3726 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3729 case SelectionEndTrim:
3730 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3733 case SelectionExtend:
3739 SelectionDrag::motion (GdkEvent* event, bool first_move)
3741 framepos_t start = 0;
3743 framecnt_t length = 0;
3744 framecnt_t distance = 0;
3746 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3747 if (pending_time_axis.first == 0) {
3751 framepos_t const pending_position = adjusted_current_frame (event);
3753 /* only alter selection if things have changed */
3755 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3759 switch (_operation) {
3760 case CreateSelection:
3762 framepos_t grab = grab_frame ();
3765 grab = adjusted_current_frame (event, false);
3766 if (grab < pending_position) {
3767 _editor->snap_to (grab, -1);
3769 _editor->snap_to (grab, 1);
3773 if (pending_position < grab) {
3774 start = pending_position;
3777 end = pending_position;
3781 /* first drag: Either add to the selection
3782 or create a new selection
3788 /* adding to the selection */
3789 _editor->set_selected_track_as_side_effect (Selection::Add);
3790 //_editor->selection->add (_editor->clicked_axisview);
3791 _editor->clicked_selection = _editor->selection->add (start, end);
3796 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3797 //_editor->selection->set (_editor->clicked_axisview);
3798 _editor->set_selected_track_as_side_effect (Selection::Set);
3801 _editor->clicked_selection = _editor->selection->set (start, end);
3805 /* select the track that we're in */
3806 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3807 // _editor->set_selected_track_as_side_effect (Selection::Add);
3808 _editor->selection->add (pending_time_axis.first);
3809 _added_time_axes.push_back (pending_time_axis.first);
3812 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3813 tracks that we selected in the first place.
3816 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3817 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3819 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3820 while (i != _added_time_axes.end()) {
3822 list<TimeAxisView*>::iterator tmp = i;
3825 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3826 _editor->selection->remove (*i);
3827 _added_time_axes.remove (*i);
3836 case SelectionStartTrim:
3838 start = _editor->selection->time[_editor->clicked_selection].start;
3839 end = _editor->selection->time[_editor->clicked_selection].end;
3841 if (pending_position > end) {
3844 start = pending_position;
3848 case SelectionEndTrim:
3850 start = _editor->selection->time[_editor->clicked_selection].start;
3851 end = _editor->selection->time[_editor->clicked_selection].end;
3853 if (pending_position < start) {
3856 end = pending_position;
3863 start = _editor->selection->time[_editor->clicked_selection].start;
3864 end = _editor->selection->time[_editor->clicked_selection].end;
3866 length = end - start;
3867 distance = pending_position - start;
3868 start = pending_position;
3869 _editor->snap_to (start);
3871 end = start + length;
3875 case SelectionExtend:
3879 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
3880 _editor->start_canvas_autoscroll (1, 0);
3884 switch (_operation) {
3886 if (_time_selection_at_start) {
3887 _editor->selection->move_time (distance);
3891 _editor->selection->replace (_editor->clicked_selection, start, end);
3895 if (_operation == SelectionMove) {
3896 show_verbose_cursor_time(start);
3898 show_verbose_cursor_time(pending_position);
3903 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3905 Session* s = _editor->session();
3907 if (movement_occurred) {
3908 motion (event, false);
3909 /* XXX this is not object-oriented programming at all. ick */
3910 if (_editor->selection->time.consolidate()) {
3911 _editor->selection->TimeChanged ();
3914 /* XXX what if its a music time selection? */
3916 if ( s->get_play_range() && s->transport_rolling() ) {
3917 s->request_play_range (&_editor->selection->time, true);
3919 if (Config->get_always_play_range() && !s->transport_rolling()) {
3920 s->request_locate (_editor->get_selection().time.start());
3926 /* just a click, no pointer movement.
3929 if (_operation == SelectionExtend) {
3930 if (_time_selection_at_start) {
3931 framepos_t pos = adjusted_current_frame (event, false);
3932 framepos_t start = min (pos, start_at_start);
3933 framepos_t end = max (pos, end_at_start);
3934 _editor->selection->set (start, end);
3937 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3938 if (_editor->clicked_selection) {
3939 _editor->selection->remove (_editor->clicked_selection);
3942 if (!_editor->clicked_selection) {
3943 _editor->selection->clear_time();
3948 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3949 _editor->selection->set (_editor->clicked_axisview);
3952 if (s && s->get_play_range () && s->transport_rolling()) {
3953 s->request_stop (false, false);
3958 _editor->stop_canvas_autoscroll ();
3959 _editor->clicked_selection = 0;
3963 SelectionDrag::aborted (bool)
3968 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3973 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3975 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
3976 ArdourCanvas::Rect (0.0, 0.0, 0.0,
3977 physical_screen_height (_editor->get_window())));
3978 _drag_rect->hide ();
3980 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3981 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3985 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3987 if (_editor->session() == 0) {
3991 Gdk::Cursor* cursor = 0;
3993 if (!_editor->temp_location) {
3994 _editor->temp_location = new Location (*_editor->session());
3997 switch (_operation) {
3998 case CreateRangeMarker:
3999 case CreateTransportMarker:
4000 case CreateCDMarker:
4002 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4007 cursor = _editor->cursors()->selector;
4011 Drag::start_grab (event, cursor);
4013 show_verbose_cursor_time (adjusted_current_frame (event));
4017 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4019 framepos_t start = 0;
4021 ArdourCanvas::Rectangle *crect;
4023 switch (_operation) {
4024 case CreateRangeMarker:
4025 crect = _editor->range_bar_drag_rect;
4027 case CreateTransportMarker:
4028 crect = _editor->transport_bar_drag_rect;
4030 case CreateCDMarker:
4031 crect = _editor->cd_marker_bar_drag_rect;
4034 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4039 framepos_t const pf = adjusted_current_frame (event);
4041 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4042 framepos_t grab = grab_frame ();
4043 _editor->snap_to (grab);
4045 if (pf < grab_frame()) {
4053 /* first drag: Either add to the selection
4054 or create a new selection.
4059 _editor->temp_location->set (start, end);
4063 update_item (_editor->temp_location);
4065 //_drag_rect->raise_to_top();
4070 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
4071 _editor->start_canvas_autoscroll (1, 0);
4075 _editor->temp_location->set (start, end);
4077 double x1 = _editor->sample_to_pixel (start);
4078 double x2 = _editor->sample_to_pixel (end);
4082 update_item (_editor->temp_location);
4085 show_verbose_cursor_time (pf);
4090 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4092 Location * newloc = 0;
4096 if (movement_occurred) {
4097 motion (event, false);
4100 switch (_operation) {
4101 case CreateRangeMarker:
4102 case CreateCDMarker:
4104 _editor->begin_reversible_command (_("new range marker"));
4105 XMLNode &before = _editor->session()->locations()->get_state();
4106 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4107 if (_operation == CreateCDMarker) {
4108 flags = Location::IsRangeMarker | Location::IsCDMarker;
4109 _editor->cd_marker_bar_drag_rect->hide();
4112 flags = Location::IsRangeMarker;
4113 _editor->range_bar_drag_rect->hide();
4115 newloc = new Location (
4116 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4119 _editor->session()->locations()->add (newloc, true);
4120 XMLNode &after = _editor->session()->locations()->get_state();
4121 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4122 _editor->commit_reversible_command ();
4126 case CreateTransportMarker:
4127 // popup menu to pick loop or punch
4128 _editor->new_transport_marker_context_menu (&event->button, _item);
4132 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4134 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
4139 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4141 if (end == max_framepos) {
4142 end = _editor->session()->current_end_frame ();
4145 if (start == max_framepos) {
4146 start = _editor->session()->current_start_frame ();
4149 switch (_editor->mouse_mode) {
4151 /* find the two markers on either side and then make the selection from it */
4152 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4156 /* find the two markers on either side of the click and make the range out of it */
4157 _editor->selection->set (start, end);
4166 _editor->stop_canvas_autoscroll ();
4170 RangeMarkerBarDrag::aborted (bool)
4176 RangeMarkerBarDrag::update_item (Location* location)
4178 double const x1 = _editor->sample_to_pixel (location->start());
4179 double const x2 = _editor->sample_to_pixel (location->end());
4181 _drag_rect->set_x0 (x1);
4182 _drag_rect->set_x1 (x2);
4185 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4189 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4193 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4195 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4196 Drag::start_grab (event, _editor->cursors()->zoom_out);
4199 Drag::start_grab (event, _editor->cursors()->zoom_in);
4203 show_verbose_cursor_time (adjusted_current_frame (event));
4207 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4212 framepos_t const pf = adjusted_current_frame (event);
4214 framepos_t grab = grab_frame ();
4215 _editor->snap_to_with_modifier (grab, event);
4217 /* base start and end on initial click position */
4229 _editor->zoom_rect->show();
4230 _editor->zoom_rect->raise_to_top();
4233 _editor->reposition_zoom_rect(start, end);
4235 show_verbose_cursor_time (pf);
4240 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4242 if (movement_occurred) {
4243 motion (event, false);
4245 if (grab_frame() < last_pointer_frame()) {
4246 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4248 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4251 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4252 _editor->tav_zoom_step (_zoom_out);
4254 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4258 _editor->zoom_rect->hide();
4262 MouseZoomDrag::aborted (bool)
4264 _editor->zoom_rect->hide ();
4267 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4269 , _cumulative_dx (0)
4270 , _cumulative_dy (0)
4272 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4274 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4276 _region = &_primary->region_view ();
4277 _note_height = _region->midi_stream_view()->note_height ();
4281 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4283 Drag::start_grab (event);
4285 if (!(_was_selected = _primary->selected())) {
4287 /* tertiary-click means extend selection - we'll do that on button release,
4288 so don't add it here, because otherwise we make it hard to figure
4289 out the "extend-to" range.
4292 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4295 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4298 _region->note_selected (_primary, true);
4300 _region->unique_select (_primary);
4306 /** @return Current total drag x change in frames */
4308 NoteDrag::total_dx () const
4311 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4313 /* primary note time */
4314 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4316 /* new time of the primary note in session frames */
4317 frameoffset_t st = n + dx;
4319 framepos_t const rp = _region->region()->position ();
4321 /* prevent the note being dragged earlier than the region's position */
4324 /* snap and return corresponding delta */
4325 return _region->snap_frame_to_frame (st - rp) + rp - n;
4328 /** @return Current total drag y change in note number */
4330 NoteDrag::total_dy () const
4332 MidiStreamView* msv = _region->midi_stream_view ();
4333 double const y = _region->midi_view()->y_position ();
4334 /* new current note */
4335 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4337 n = max (msv->lowest_note(), n);
4338 n = min (msv->highest_note(), n);
4339 /* and work out delta */
4340 return n - msv->y_to_note (grab_y() - y);
4344 NoteDrag::motion (GdkEvent *, bool)
4346 /* Total change in x and y since the start of the drag */
4347 frameoffset_t const dx = total_dx ();
4348 int8_t const dy = total_dy ();
4350 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4351 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4352 double const tdy = -dy * _note_height - _cumulative_dy;
4355 _cumulative_dx += tdx;
4356 _cumulative_dy += tdy;
4358 int8_t note_delta = total_dy();
4360 _region->move_selection (tdx, tdy, note_delta);
4362 /* the new note value may be the same as the old one, but we
4363 * don't know what that means because the selection may have
4364 * involved more than one note and we might be doing something
4365 * odd with them. so show the note value anyway, always.
4369 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4371 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4372 (int) floor ((double)new_note));
4374 show_verbose_cursor_text (buf);
4379 NoteDrag::finished (GdkEvent* ev, bool moved)
4382 /* no motion - select note */
4384 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4385 _editor->current_mouse_mode() == Editing::MouseDraw) {
4387 if (_was_selected) {
4388 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4390 _region->note_deselected (_primary);
4393 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4394 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4396 if (!extend && !add && _region->selection_size() > 1) {
4397 _region->unique_select (_primary);
4398 } else if (extend) {
4399 _region->note_selected (_primary, true, true);
4401 /* it was added during button press */
4406 _region->note_dropped (_primary, total_dx(), total_dy());
4411 NoteDrag::aborted (bool)
4416 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4417 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4418 : Drag (editor, atv->base_item ())
4420 , _nothing_to_drag (false)
4422 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4423 y_origin = atv->y_position();
4424 setup (atv->lines ());
4427 /** Make an AutomationRangeDrag for region gain lines */
4428 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4429 : Drag (editor, rv->get_canvas_group ())
4431 , _nothing_to_drag (false)
4433 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4435 list<boost::shared_ptr<AutomationLine> > lines;
4436 lines.push_back (rv->get_gain_line ());
4437 y_origin = rv->get_time_axis_view().y_position();
4441 /** @param lines AutomationLines to drag.
4442 * @param offset Offset from the session start to the points in the AutomationLines.
4445 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4447 /* find the lines that overlap the ranges being dragged */
4448 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4449 while (i != lines.end ()) {
4450 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4453 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4455 /* check this range against all the AudioRanges that we are using */
4456 list<AudioRange>::const_iterator k = _ranges.begin ();
4457 while (k != _ranges.end()) {
4458 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4464 /* add it to our list if it overlaps at all */
4465 if (k != _ranges.end()) {
4470 _lines.push_back (n);
4476 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4480 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4482 return 1.0 - ((global_y - y_origin) / line->height());
4486 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4488 Drag::start_grab (event, cursor);
4490 /* Get line states before we start changing things */
4491 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4492 i->state = &i->line->get_state ();
4493 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4496 if (_ranges.empty()) {
4498 /* No selected time ranges: drag all points */
4499 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4500 uint32_t const N = i->line->npoints ();
4501 for (uint32_t j = 0; j < N; ++j) {
4502 i->points.push_back (i->line->nth (j));
4508 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4510 framecnt_t const half = (i->start + i->end) / 2;
4512 /* find the line that this audio range starts in */
4513 list<Line>::iterator j = _lines.begin();
4514 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4518 if (j != _lines.end()) {
4519 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4521 /* j is the line that this audio range starts in; fade into it;
4522 64 samples length plucked out of thin air.
4525 framepos_t a = i->start + 64;
4530 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4531 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4533 the_list->add (p, the_list->eval (p));
4534 the_list->add (q, the_list->eval (q));
4537 /* same thing for the end */
4540 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4544 if (j != _lines.end()) {
4545 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4547 /* j is the line that this audio range starts in; fade out of it;
4548 64 samples length plucked out of thin air.
4551 framepos_t b = i->end - 64;
4556 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4557 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4559 the_list->add (p, the_list->eval (p));
4560 the_list->add (q, the_list->eval (q));
4564 _nothing_to_drag = true;
4566 /* Find all the points that should be dragged and put them in the relevant
4567 points lists in the Line structs.
4570 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4572 uint32_t const N = i->line->npoints ();
4573 for (uint32_t j = 0; j < N; ++j) {
4575 /* here's a control point on this line */
4576 ControlPoint* p = i->line->nth (j);
4577 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4579 /* see if it's inside a range */
4580 list<AudioRange>::const_iterator k = _ranges.begin ();
4581 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4585 if (k != _ranges.end()) {
4586 /* dragging this point */
4587 _nothing_to_drag = false;
4588 i->points.push_back (p);
4594 if (_nothing_to_drag) {
4598 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4599 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4604 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4606 if (_nothing_to_drag) {
4610 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4611 float const f = y_fraction (l->line, _drags->current_pointer_y());
4612 /* we are ignoring x position for this drag, so we can just pass in anything */
4614 l->line->drag_motion (0, f, true, false, ignored);
4615 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4620 AutomationRangeDrag::finished (GdkEvent* event, bool)
4622 if (_nothing_to_drag) {
4626 motion (event, false);
4627 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4628 i->line->end_drag (false, 0);
4631 _editor->session()->commit_reversible_command ();
4635 AutomationRangeDrag::aborted (bool)
4637 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4642 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4645 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4646 layer = v->region()->layer ();
4647 initial_y = v->get_canvas_group()->position().y;
4648 initial_playlist = v->region()->playlist ();
4649 initial_position = v->region()->position ();
4650 initial_end = v->region()->position () + v->region()->length ();
4653 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4654 : Drag (e, i->canvas_item ())
4657 , _cumulative_dx (0)
4659 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4660 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4665 PatchChangeDrag::motion (GdkEvent* ev, bool)
4667 framepos_t f = adjusted_current_frame (ev);
4668 boost::shared_ptr<Region> r = _region_view->region ();
4669 f = max (f, r->position ());
4670 f = min (f, r->last_frame ());
4672 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4673 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4674 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4675 _cumulative_dx = dxu;
4679 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4681 if (!movement_occurred) {
4685 boost::shared_ptr<Region> r (_region_view->region ());
4686 framepos_t f = adjusted_current_frame (ev);
4687 f = max (f, r->position ());
4688 f = min (f, r->last_frame ());
4690 _region_view->move_patch_change (
4692 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4697 PatchChangeDrag::aborted (bool)
4699 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4703 PatchChangeDrag::setup_pointer_frame_offset ()
4705 boost::shared_ptr<Region> region = _region_view->region ();
4706 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4709 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4710 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4717 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4719 framepos_t const p = _region_view->region()->position ();
4720 double const y = _region_view->midi_view()->y_position ();
4722 x1 = max ((framepos_t) 0, x1 - p);
4723 x2 = max ((framepos_t) 0, x2 - p);
4724 y1 = max (0.0, y1 - y);
4725 y2 = max (0.0, y2 - y);
4727 _region_view->update_drag_selection (
4728 _editor->sample_to_pixel (x1),
4729 _editor->sample_to_pixel (x2),
4732 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4737 MidiRubberbandSelectDrag::deselect_things ()
4742 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4743 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4746 _vertical_only = true;
4750 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4752 double const y = _region_view->midi_view()->y_position ();
4754 y1 = max (0.0, y1 - y);
4755 y2 = max (0.0, y2 - y);
4757 _region_view->update_vertical_drag_selection (
4760 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4765 MidiVerticalSelectDrag::deselect_things ()
4770 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4771 : RubberbandSelectDrag (e, i)
4777 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4779 if (drag_in_progress) {
4780 /* We just want to select things at the end of the drag, not during it */
4784 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4786 _editor->begin_reversible_command (_("rubberband selection"));
4787 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4788 _editor->commit_reversible_command ();
4792 EditorRubberbandSelectDrag::deselect_things ()
4794 if (!getenv("ARDOUR_SAE")) {
4795 _editor->selection->clear_tracks();
4797 _editor->selection->clear_regions();
4798 _editor->selection->clear_points ();
4799 _editor->selection->clear_lines ();
4802 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4810 NoteCreateDrag::~NoteCreateDrag ()
4816 NoteCreateDrag::grid_frames (framepos_t t) const
4819 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4824 return _region_view->region_beats_to_region_frames (grid_beats);
4828 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4830 Drag::start_grab (event, cursor);
4832 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4834 framepos_t pf = _drags->current_pointer_frame ();
4835 framecnt_t const g = grid_frames (pf);
4837 /* Hack so that we always snap to the note that we are over, instead of snapping
4838 to the next one if we're more than halfway through the one we're over.
4840 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4844 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4846 MidiStreamView* sv = _region_view->midi_stream_view ();
4847 double const x = _editor->sample_to_pixel (_note[0]);
4848 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4850 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4851 _drag_rect->set_outline_what (0xff);
4852 _drag_rect->set_outline_color (0xffffff99);
4853 _drag_rect->set_fill_color (0xffffff66);
4857 NoteCreateDrag::motion (GdkEvent* event, bool)
4859 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4860 double const x = _editor->sample_to_pixel (_note[1]);
4861 if (_note[1] > _note[0]) {
4862 _drag_rect->set_x1 (x);
4864 _drag_rect->set_x0 (x);
4869 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4871 if (!had_movement) {
4875 framepos_t const start = min (_note[0], _note[1]);
4876 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
4878 framecnt_t const g = grid_frames (start);
4879 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4881 if (_editor->snap_mode() == SnapNormal && length < g) {
4882 length = g - one_tick;
4885 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4887 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
4891 NoteCreateDrag::y_to_region (double y) const
4894 _region_view->get_canvas_group()->canvas_to_item (x, y);
4899 NoteCreateDrag::aborted (bool)
4904 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4909 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
4913 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4915 Drag::start_grab (event, cursor);
4919 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4925 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4928 distance = _drags->current_pointer_x() - grab_x();
4929 len = ar->fade_in()->back()->when;
4931 distance = grab_x() - _drags->current_pointer_x();
4932 len = ar->fade_out()->back()->when;
4935 /* how long should it be ? */
4937 new_length = len + _editor->pixel_to_sample (distance);
4939 /* now check with the region that this is legal */
4941 new_length = ar->verify_xfade_bounds (new_length, start);
4944 arv->reset_fade_in_shape_width (ar, new_length);
4946 arv->reset_fade_out_shape_width (ar, new_length);
4951 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
4957 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4960 distance = _drags->current_pointer_x() - grab_x();
4961 len = ar->fade_in()->back()->when;
4963 distance = grab_x() - _drags->current_pointer_x();
4964 len = ar->fade_out()->back()->when;
4967 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
4969 _editor->begin_reversible_command ("xfade trim");
4970 ar->playlist()->clear_owned_changes ();
4973 ar->set_fade_in_length (new_length);
4975 ar->set_fade_out_length (new_length);
4978 /* Adjusting the xfade may affect other regions in the playlist, so we need
4979 to get undo Commands from the whole playlist rather than just the
4983 vector<Command*> cmds;
4984 ar->playlist()->rdiff (cmds);
4985 _editor->session()->add_commands (cmds);
4986 _editor->commit_reversible_command ();
4991 CrossfadeEdgeDrag::aborted (bool)
4994 arv->redraw_start_xfade ();
4996 arv->redraw_end_xfade ();