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_sample (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_sample (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_sample (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_sample (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 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
367 if (!_editor->autoscroll_active() || from_autoscroll) {
368 motion (event, _move_threshold_passed != old_move_threshold_passed);
370 _last_pointer_x = _drags->current_pointer_x ();
371 _last_pointer_y = _drags->current_pointer_y ();
372 _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) {
878 if (was_double_click() && !_views.empty()) {
879 DraggingView dv = _views.front();
880 dv.view->show_region_editor ();
887 /* reverse this here so that we have the correct logic to finalize
891 if (Config->get_edit_mode() == Lock) {
892 _x_constrained = !_x_constrained;
895 assert (!_views.empty ());
897 /* We might have hidden region views so that they weren't visible during the drag
898 (when they have been reparented). Now everything can be shown again, as region
899 views are back in their track parent groups.
901 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
902 i->view->get_canvas_group()->show ();
905 bool const changed_position = (_last_frame_position != _primary->region()->position());
906 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
907 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
927 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
931 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
933 RegionSelection new_views;
934 PlaylistSet modified_playlists;
935 list<RegionView*> views_to_delete;
938 /* all changes were made during motion event handlers */
940 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
944 _editor->commit_reversible_command ();
948 if (_x_constrained) {
949 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
951 _editor->begin_reversible_command (Operations::region_copy);
954 /* insert the regions into their new playlists */
955 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
957 if (i->view->region()->locked() || i->view->region()->video_locked()) {
963 if (changed_position && !_x_constrained) {
964 where = i->view->region()->position() - drag_delta;
966 where = i->view->region()->position();
969 RegionView* new_view = insert_region_into_playlist (
970 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
977 new_views.push_back (new_view);
979 /* we don't need the copied RegionView any more */
980 views_to_delete.push_back (i->view);
983 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
984 because when views are deleted they are automagically removed from _views, which messes
987 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
991 /* If we've created new regions either by copying or moving
992 to a new track, we want to replace the old selection with the new ones
995 if (new_views.size() > 0) {
996 _editor->selection->set (new_views);
999 /* write commands for the accumulated diffs for all our modified playlists */
1000 add_stateful_diff_commands_for_playlists (modified_playlists);
1002 _editor->commit_reversible_command ();
1006 RegionMoveDrag::finished_no_copy (
1007 bool const changed_position,
1008 bool const changed_tracks,
1009 framecnt_t const drag_delta
1012 RegionSelection new_views;
1013 PlaylistSet modified_playlists;
1014 PlaylistSet frozen_playlists;
1015 set<RouteTimeAxisView*> views_to_update;
1018 /* all changes were made during motion event handlers */
1019 _editor->commit_reversible_command ();
1023 if (_x_constrained) {
1024 _editor->begin_reversible_command (_("fixed time region drag"));
1026 _editor->begin_reversible_command (Operations::region_drag);
1029 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1031 RegionView* rv = i->view;
1033 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1034 double const dest_layer = i->layer;
1036 if (rv->region()->locked() || rv->region()->video_locked()) {
1041 views_to_update.insert (dest_rtv);
1045 if (changed_position && !_x_constrained) {
1046 where = rv->region()->position() - drag_delta;
1048 where = rv->region()->position();
1051 if (changed_tracks) {
1053 /* insert into new playlist */
1055 RegionView* new_view = insert_region_into_playlist (
1056 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1059 if (new_view == 0) {
1064 new_views.push_back (new_view);
1066 /* remove from old playlist */
1068 /* the region that used to be in the old playlist is not
1069 moved to the new one - we use a copy of it. as a result,
1070 any existing editor for the region should no longer be
1073 rv->hide_region_editor();
1074 rv->fake_set_opaque (false);
1076 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1080 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1082 /* this movement may result in a crossfade being modified, or a layering change,
1083 so we need to get undo data from the playlist as well as the region.
1086 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1088 playlist->clear_changes ();
1091 rv->region()->clear_changes ();
1094 motion on the same track. plonk the previously reparented region
1095 back to its original canvas group (its streamview).
1096 No need to do anything for copies as they are fake regions which will be deleted.
1099 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1100 rv->get_canvas_group()->set_y_position (i->initial_y);
1103 /* just change the model */
1104 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1105 playlist->set_layer (rv->region(), dest_layer);
1108 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1110 r = frozen_playlists.insert (playlist);
1113 playlist->freeze ();
1116 rv->region()->set_position (where);
1118 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1121 if (changed_tracks) {
1123 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1124 was selected in all of them, then removing it from a playlist will have removed all
1125 trace of it from _views (i.e. there were N regions selected, we removed 1,
1126 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1127 corresponding regionview, and _views is now empty).
1129 This could have invalidated any and all iterators into _views.
1131 The heuristic we use here is: if the region selection is empty, break out of the loop
1132 here. if the region selection is not empty, then restart the loop because we know that
1133 we must have removed at least the region(view) we've just been working on as well as any
1134 that we processed on previous iterations.
1136 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1137 we can just iterate.
1141 if (_views.empty()) {
1152 /* If we've created new regions either by copying or moving
1153 to a new track, we want to replace the old selection with the new ones
1156 if (new_views.size() > 0) {
1157 _editor->selection->set (new_views);
1160 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1164 /* write commands for the accumulated diffs for all our modified playlists */
1165 add_stateful_diff_commands_for_playlists (modified_playlists);
1167 _editor->commit_reversible_command ();
1169 /* We have futzed with the layering of canvas items on our streamviews.
1170 If any region changed layer, this will have resulted in the stream
1171 views being asked to set up their region views, and all will be well.
1172 If not, we might now have badly-ordered region views. Ask the StreamViews
1173 involved to sort themselves out, just in case.
1176 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1177 (*i)->view()->playlist_layered ((*i)->track ());
1181 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1182 * @param region Region to remove.
1183 * @param playlist playlist To remove from.
1184 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1185 * that clear_changes () is only called once per playlist.
1188 RegionMoveDrag::remove_region_from_playlist (
1189 boost::shared_ptr<Region> region,
1190 boost::shared_ptr<Playlist> playlist,
1191 PlaylistSet& modified_playlists
1194 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1197 playlist->clear_changes ();
1200 playlist->remove_region (region);
1204 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1205 * clearing the playlist's diff history first if necessary.
1206 * @param region Region to insert.
1207 * @param dest_rtv Destination RouteTimeAxisView.
1208 * @param dest_layer Destination layer.
1209 * @param where Destination position.
1210 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1211 * that clear_changes () is only called once per playlist.
1212 * @return New RegionView, or 0 if no insert was performed.
1215 RegionMoveDrag::insert_region_into_playlist (
1216 boost::shared_ptr<Region> region,
1217 RouteTimeAxisView* dest_rtv,
1220 PlaylistSet& modified_playlists
1223 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1224 if (!dest_playlist) {
1228 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1229 _new_region_view = 0;
1230 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1232 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1233 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1235 dest_playlist->clear_changes ();
1238 dest_playlist->add_region (region, where);
1240 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1241 dest_playlist->set_layer (region, dest_layer);
1246 assert (_new_region_view);
1248 return _new_region_view;
1252 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1254 _new_region_view = rv;
1258 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1260 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1261 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1263 _editor->session()->add_command (c);
1272 RegionMoveDrag::aborted (bool movement_occurred)
1276 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1283 RegionMotionDrag::aborted (movement_occurred);
1288 RegionMotionDrag::aborted (bool)
1290 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1292 StreamView* sview = (*i)->view();
1295 if (sview->layer_display() == Expanded) {
1296 sview->set_layer_display (Stacked);
1301 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1302 RegionView* rv = i->view;
1303 TimeAxisView* tv = &(rv->get_time_axis_view ());
1304 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1306 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1307 rv->get_canvas_group()->set_y_position (0);
1309 rv->fake_set_opaque (false);
1310 rv->move (-_total_x_delta, 0);
1311 rv->set_height (rtv->view()->child_height ());
1315 /** @param b true to brush, otherwise false.
1316 * @param c true to make copies of the regions being moved, otherwise false.
1318 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1319 : RegionMotionDrag (e, i, p, v, b),
1322 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1325 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1326 if (rtv && rtv->is_track()) {
1327 speed = rtv->track()->speed ();
1330 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1334 RegionMoveDrag::setup_pointer_frame_offset ()
1336 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1339 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1340 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1342 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1344 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1345 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1347 _primary = v->view()->create_region_view (r, false, false);
1349 _primary->get_canvas_group()->show ();
1350 _primary->set_position (pos, 0);
1351 _views.push_back (DraggingView (_primary, this));
1353 _last_frame_position = pos;
1355 _item = _primary->get_canvas_group ();
1359 RegionInsertDrag::finished (GdkEvent *, bool)
1361 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1363 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1364 _primary->get_canvas_group()->set_y_position (0);
1366 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1368 _editor->begin_reversible_command (Operations::insert_region);
1369 playlist->clear_changes ();
1370 playlist->add_region (_primary->region (), _last_frame_position);
1371 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1372 _editor->commit_reversible_command ();
1380 RegionInsertDrag::aborted (bool)
1387 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1388 : RegionMoveDrag (e, i, p, v, false, false)
1390 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1393 struct RegionSelectionByPosition {
1394 bool operator() (RegionView*a, RegionView* b) {
1395 return a->region()->position () < b->region()->position();
1400 RegionSpliceDrag::motion (GdkEvent* event, bool)
1402 /* Which trackview is this ? */
1404 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1405 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1407 /* The region motion is only processed if the pointer is over
1411 if (!tv || !tv->is_track()) {
1412 /* To make sure we hide the verbose canvas cursor when the mouse is
1413 not held over and audiotrack.
1415 _editor->verbose_cursor()->hide ();
1421 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1427 RegionSelection copy (_editor->selection->regions);
1429 RegionSelectionByPosition cmp;
1432 framepos_t const pf = adjusted_current_frame (event);
1434 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1436 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1442 boost::shared_ptr<Playlist> playlist;
1444 if ((playlist = atv->playlist()) == 0) {
1448 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1453 if (pf < (*i)->region()->last_frame() + 1) {
1457 if (pf > (*i)->region()->first_frame()) {
1463 playlist->shuffle ((*i)->region(), dir);
1468 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1470 RegionMoveDrag::finished (event, movement_occurred);
1474 RegionSpliceDrag::aborted (bool)
1479 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1481 _view (dynamic_cast<MidiTimeAxisView*> (v))
1483 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1489 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1492 _region = add_midi_region (_view);
1493 _view->playlist()->freeze ();
1496 framepos_t const f = adjusted_current_frame (event);
1497 if (f < grab_frame()) {
1498 _region->set_position (f);
1501 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1502 so that if this region is duplicated, its duplicate starts on
1503 a snap point rather than 1 frame after a snap point. Otherwise things get
1504 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1505 place snapped notes at the start of the region.
1508 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1509 _region->set_length (len < 1 ? 1 : len);
1515 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1517 if (!movement_occurred) {
1518 add_midi_region (_view);
1520 _view->playlist()->thaw ();
1525 RegionCreateDrag::aborted (bool)
1528 _view->playlist()->thaw ();
1534 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1538 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1542 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1544 Gdk::Cursor* cursor;
1545 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1547 float x_fraction = cnote->mouse_x_fraction ();
1549 if (x_fraction > 0.0 && x_fraction < 0.25) {
1550 cursor = _editor->cursors()->left_side_trim;
1552 cursor = _editor->cursors()->right_side_trim;
1555 Drag::start_grab (event, cursor);
1557 region = &cnote->region_view();
1559 double const region_start = region->get_position_pixels();
1560 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1562 if (grab_x() <= middle_point) {
1563 cursor = _editor->cursors()->left_side_trim;
1566 cursor = _editor->cursors()->right_side_trim;
1572 if (event->motion.state & Keyboard::PrimaryModifier) {
1578 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1580 if (ms.size() > 1) {
1581 /* has to be relative, may make no sense otherwise */
1585 /* select this note; if it is already selected, preserve the existing selection,
1586 otherwise make this note the only one selected.
1588 region->note_selected (cnote, cnote->selected ());
1590 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1591 MidiRegionSelection::iterator next;
1594 (*r)->begin_resizing (at_front);
1600 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1602 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1603 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1604 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1606 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1611 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1613 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1614 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1615 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1617 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1622 NoteResizeDrag::aborted (bool)
1624 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1625 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1626 (*r)->abort_resizing ();
1630 AVDraggingView::AVDraggingView (RegionView* v)
1633 initial_position = v->region()->position ();
1636 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1639 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1642 TrackViewList empty;
1644 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1645 std::list<RegionView*> views = rs.by_layer();
1647 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1648 RegionView* rv = (*i);
1649 if (!rv->region()->video_locked()) {
1652 _views.push_back (AVDraggingView (rv));
1657 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1659 Drag::start_grab (event);
1660 if (_editor->session() == 0) {
1664 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1665 _max_backwards_drag = (
1666 ARDOUR_UI::instance()->video_timeline->get_duration()
1667 + ARDOUR_UI::instance()->video_timeline->get_offset()
1668 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1671 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1672 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1673 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1676 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1679 Timecode::Time timecode;
1680 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1681 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);
1682 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1683 _editor->verbose_cursor()->show ();
1687 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1689 if (_editor->session() == 0) {
1692 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1696 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1697 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
1699 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1700 dt = - _max_backwards_drag;
1703 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1704 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1706 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1707 RegionView* rv = i->view;
1708 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1711 rv->fake_set_opaque (true);
1712 rv->region()->clear_changes ();
1713 rv->region()->suspend_property_changes();
1715 rv->region()->set_position(i->initial_position + dt);
1716 rv->region_changed(ARDOUR::Properties::position);
1719 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1720 Timecode::Time timecode;
1721 Timecode::Time timediff;
1723 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1724 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1725 snprintf (buf, sizeof (buf),
1726 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1727 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1728 , _("Video Start:"),
1729 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1731 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1733 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1734 _editor->verbose_cursor()->show ();
1738 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1740 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1744 if (!movement_occurred || ! _editor->session()) {
1748 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1750 _editor->begin_reversible_command (_("Move Video"));
1752 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1753 ARDOUR_UI::instance()->video_timeline->save_undo();
1754 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1755 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1757 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1758 i->view->drag_end();
1759 i->view->fake_set_opaque (false);
1760 i->view->region()->resume_property_changes ();
1762 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1765 _editor->session()->maybe_update_session_range(
1766 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1767 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1771 _editor->commit_reversible_command ();
1775 VideoTimeLineDrag::aborted (bool)
1777 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1780 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1781 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1783 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1784 i->view->region()->resume_property_changes ();
1785 i->view->region()->set_position(i->initial_position);
1789 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1790 : RegionDrag (e, i, p, v)
1791 , _preserve_fade_anchor (preserve_fade_anchor)
1792 , _jump_position_when_done (false)
1794 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1798 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1801 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1802 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1804 if (tv && tv->is_track()) {
1805 speed = tv->track()->speed();
1808 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1809 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1810 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1812 framepos_t const pf = adjusted_current_frame (event);
1814 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1815 /* Move the contents of the region around without changing the region bounds */
1816 _operation = ContentsTrim;
1817 Drag::start_grab (event, _editor->cursors()->trimmer);
1819 /* These will get overridden for a point trim.*/
1820 if (pf < (region_start + region_length/2)) {
1821 /* closer to front */
1822 _operation = StartTrim;
1823 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1826 _operation = EndTrim;
1827 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1831 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1832 _jump_position_when_done = true;
1835 switch (_operation) {
1837 show_verbose_cursor_time (region_start);
1838 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1839 i->view->trim_front_starting ();
1843 show_verbose_cursor_time (region_end);
1846 show_verbose_cursor_time (pf);
1850 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1851 i->view->region()->suspend_property_changes ();
1856 TrimDrag::motion (GdkEvent* event, bool first_move)
1858 RegionView* rv = _primary;
1861 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1862 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1863 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1864 frameoffset_t frame_delta = 0;
1866 if (tv && tv->is_track()) {
1867 speed = tv->track()->speed();
1870 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1876 switch (_operation) {
1878 trim_type = "Region start trim";
1881 trim_type = "Region end trim";
1884 trim_type = "Region content trim";
1888 _editor->begin_reversible_command (trim_type);
1890 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1891 RegionView* rv = i->view;
1892 rv->fake_set_opaque (false);
1893 rv->enable_display (false);
1894 rv->region()->playlist()->clear_owned_changes ();
1896 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1899 arv->temporarily_hide_envelope ();
1903 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1904 insert_result = _editor->motion_frozen_playlists.insert (pl);
1906 if (insert_result.second) {
1912 bool non_overlap_trim = false;
1914 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1915 non_overlap_trim = true;
1918 switch (_operation) {
1920 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1921 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1922 if (changed && _preserve_fade_anchor) {
1923 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1928 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1929 distance = _drags->current_pointer_x() - grab_x();
1930 len = ar->fade_in()->back()->when;
1931 new_length = len - _editor->pixel_to_sample (distance);
1932 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
1933 arv->reset_fade_in_shape_width (ar, new_length); //the grey shape
1940 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1941 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1942 if (changed && _preserve_fade_anchor) {
1943 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1948 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1949 distance = grab_x() - _drags->current_pointer_x();
1950 len = ar->fade_out()->back()->when;
1951 new_length = len - _editor->pixel_to_sample (distance);
1952 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
1953 arv->reset_fade_out_shape_width (ar, new_length); //the grey shape
1961 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1963 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1964 i->view->move_contents (frame_delta);
1970 switch (_operation) {
1972 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1975 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1978 // show_verbose_cursor_time (frame_delta);
1985 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1987 if (movement_occurred) {
1988 motion (event, false);
1990 if (_operation == StartTrim) {
1991 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1993 /* This must happen before the region's StatefulDiffCommand is created, as it may
1994 `correct' (ahem) the region's _start from being negative to being zero. It
1995 needs to be zero in the undo record.
1997 i->view->trim_front_ending ();
1999 if (_preserve_fade_anchor) {
2000 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2005 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2006 distance = _drags->current_pointer_x() - grab_x();
2007 len = ar->fade_in()->back()->when;
2008 new_length = len - _editor->pixel_to_sample (distance);
2009 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2010 ar->set_fade_in_length(new_length);
2013 if (_jump_position_when_done) {
2014 i->view->region()->set_position (i->initial_position);
2017 } else if (_operation == EndTrim) {
2018 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2019 if (_preserve_fade_anchor) {
2020 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2025 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2026 distance = _drags->current_pointer_x() - grab_x();
2027 len = ar->fade_out()->back()->when;
2028 new_length = len - _editor->pixel_to_sample (distance);
2029 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2030 ar->set_fade_out_length(new_length);
2033 if (_jump_position_when_done) {
2034 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2039 if (!_views.empty()) {
2040 if (_operation == StartTrim) {
2041 _editor->maybe_locate_with_edit_preroll(
2042 _views.begin()->view->region()->position());
2044 if (_operation == EndTrim) {
2045 _editor->maybe_locate_with_edit_preroll(
2046 _views.begin()->view->region()->position() +
2047 _views.begin()->view->region()->length());
2051 if (!_editor->selection->selected (_primary)) {
2052 _primary->thaw_after_trim ();
2055 set<boost::shared_ptr<Playlist> > diffed_playlists;
2057 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2058 i->view->thaw_after_trim ();
2059 i->view->enable_display (true);
2060 i->view->fake_set_opaque (true);
2062 /* Trimming one region may affect others on the playlist, so we need
2063 to get undo Commands from the whole playlist rather than just the
2064 region. Use diffed_playlists to make sure we don't diff a given
2065 playlist more than once.
2067 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2068 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2069 vector<Command*> cmds;
2071 _editor->session()->add_commands (cmds);
2072 diffed_playlists.insert (p);
2077 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2081 _editor->motion_frozen_playlists.clear ();
2082 _editor->commit_reversible_command();
2085 /* no mouse movement */
2086 _editor->point_trim (event, adjusted_current_frame (event));
2089 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2090 if (_operation == StartTrim) {
2091 i->view->trim_front_ending ();
2094 i->view->region()->resume_property_changes ();
2099 TrimDrag::aborted (bool movement_occurred)
2101 /* Our motion method is changing model state, so use the Undo system
2102 to cancel. Perhaps not ideal, as this will leave an Undo point
2103 behind which may be slightly odd from the user's point of view.
2108 if (movement_occurred) {
2112 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2113 i->view->region()->resume_property_changes ();
2118 TrimDrag::setup_pointer_frame_offset ()
2120 list<DraggingView>::iterator i = _views.begin ();
2121 while (i != _views.end() && i->view != _primary) {
2125 if (i == _views.end()) {
2129 switch (_operation) {
2131 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2134 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2141 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2145 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2146 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2151 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2153 Drag::start_grab (event, cursor);
2154 show_verbose_cursor_time (adjusted_current_frame(event));
2158 MeterMarkerDrag::setup_pointer_frame_offset ()
2160 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2164 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2166 if (!_marker->meter().movable()) {
2172 // create a dummy marker for visual representation of moving the
2173 // section, because whether its a copy or not, we're going to
2174 // leave or lose the original marker (leave if its a copy; lose if its
2175 // not, because we'll remove it from the map).
2177 MeterSection section (_marker->meter());
2179 if (!section.movable()) {
2184 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2186 _marker = new MeterMarker (
2188 *_editor->meter_group,
2189 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2191 *new MeterSection (_marker->meter())
2194 /* use the new marker for the grab */
2195 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2198 TempoMap& map (_editor->session()->tempo_map());
2199 /* get current state */
2200 before_state = &map.get_state();
2201 /* remove the section while we drag it */
2202 map.remove_meter (section, true);
2206 framepos_t const pf = adjusted_current_frame (event);
2207 _marker->set_position (pf);
2208 show_verbose_cursor_time (pf);
2212 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2214 if (!movement_occurred) {
2215 if (was_double_click()) {
2216 _editor->edit_meter_marker (*_marker);
2221 if (!_marker->meter().movable()) {
2225 motion (event, false);
2227 Timecode::BBT_Time when;
2229 TempoMap& map (_editor->session()->tempo_map());
2230 map.bbt_time (last_pointer_frame(), when);
2232 if (_copy == true) {
2233 _editor->begin_reversible_command (_("copy meter mark"));
2234 XMLNode &before = map.get_state();
2235 map.add_meter (_marker->meter(), when);
2236 XMLNode &after = map.get_state();
2237 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2238 _editor->commit_reversible_command ();
2241 _editor->begin_reversible_command (_("move meter mark"));
2243 /* we removed it before, so add it back now */
2245 map.add_meter (_marker->meter(), when);
2246 XMLNode &after = map.get_state();
2247 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2248 _editor->commit_reversible_command ();
2251 // delete the dummy marker we used for visual representation while moving.
2252 // a new visual marker will show up automatically.
2257 MeterMarkerDrag::aborted (bool moved)
2259 _marker->set_position (_marker->meter().frame ());
2262 TempoMap& map (_editor->session()->tempo_map());
2263 /* we removed it before, so add it back now */
2264 map.add_meter (_marker->meter(), _marker->meter().frame());
2265 // delete the dummy marker we used for visual representation while moving.
2266 // a new visual marker will show up automatically.
2271 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2275 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2277 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2282 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2284 Drag::start_grab (event, cursor);
2285 show_verbose_cursor_time (adjusted_current_frame (event));
2289 TempoMarkerDrag::setup_pointer_frame_offset ()
2291 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2295 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2297 if (!_marker->tempo().movable()) {
2303 // create a dummy marker for visual representation of moving the
2304 // section, because whether its a copy or not, we're going to
2305 // leave or lose the original marker (leave if its a copy; lose if its
2306 // not, because we'll remove it from the map).
2308 // create a dummy marker for visual representation of moving the copy.
2309 // The actual copying is not done before we reach the finish callback.
2312 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2314 TempoSection section (_marker->tempo());
2316 _marker = new TempoMarker (
2318 *_editor->tempo_group,
2319 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2321 *new TempoSection (_marker->tempo())
2324 /* use the new marker for the grab */
2325 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2328 TempoMap& map (_editor->session()->tempo_map());
2329 /* get current state */
2330 before_state = &map.get_state();
2331 /* remove the section while we drag it */
2332 map.remove_tempo (section, true);
2336 framepos_t const pf = adjusted_current_frame (event);
2337 _marker->set_position (pf);
2338 show_verbose_cursor_time (pf);
2342 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2344 if (!movement_occurred) {
2345 if (was_double_click()) {
2346 _editor->edit_tempo_marker (*_marker);
2351 if (!_marker->tempo().movable()) {
2355 motion (event, false);
2357 TempoMap& map (_editor->session()->tempo_map());
2358 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2359 Timecode::BBT_Time when;
2361 map.bbt_time (beat_time, when);
2363 if (_copy == true) {
2364 _editor->begin_reversible_command (_("copy tempo mark"));
2365 XMLNode &before = map.get_state();
2366 map.add_tempo (_marker->tempo(), when);
2367 XMLNode &after = map.get_state();
2368 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2369 _editor->commit_reversible_command ();
2372 _editor->begin_reversible_command (_("move tempo mark"));
2373 /* we removed it before, so add it back now */
2374 map.add_tempo (_marker->tempo(), when);
2375 XMLNode &after = map.get_state();
2376 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2377 _editor->commit_reversible_command ();
2380 // delete the dummy marker we used for visual representation while moving.
2381 // a new visual marker will show up automatically.
2386 TempoMarkerDrag::aborted (bool moved)
2388 _marker->set_position (_marker->tempo().frame());
2390 TempoMap& map (_editor->session()->tempo_map());
2391 /* we removed it before, so add it back now */
2392 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2393 // delete the dummy marker we used for visual representation while moving.
2394 // a new visual marker will show up automatically.
2399 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2400 : Drag (e, &c.time_bar_canvas_item())
2404 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2407 /** Do all the things we do when dragging the playhead to make it look as though
2408 * we have located, without actually doing the locate (because that would cause
2409 * the diskstream buffers to be refilled, which is too slow).
2412 CursorDrag::fake_locate (framepos_t t)
2414 _editor->playhead_cursor->set_position (t);
2416 Session* s = _editor->session ();
2417 if (s->timecode_transmission_suspended ()) {
2418 framepos_t const f = _editor->playhead_cursor->current_frame ();
2419 /* This is asynchronous so it will be sent "now"
2421 s->send_mmc_locate (f);
2422 /* These are synchronous and will be sent during the next
2425 s->queue_full_time_code ();
2426 s->queue_song_position_pointer ();
2429 show_verbose_cursor_time (t);
2430 _editor->UpdateAllTransportClocks (t);
2434 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2436 Drag::start_grab (event, c);
2438 _grab_zoom = _editor->samples_per_pixel;
2440 framepos_t where = _editor->canvas_event_sample (event);
2442 _editor->snap_to_with_modifier (where, event);
2444 _editor->_dragging_playhead = true;
2446 Session* s = _editor->session ();
2448 /* grab the track canvas item as well */
2450 _cursor.track_canvas_item().grab();
2453 if (_was_rolling && _stop) {
2457 if (s->is_auditioning()) {
2458 s->cancel_audition ();
2462 if (AudioEngine::instance()->connected()) {
2464 /* do this only if we're the engine is connected
2465 * because otherwise this request will never be
2466 * serviced and we'll busy wait forever. likewise,
2467 * notice if we are disconnected while waiting for the
2468 * request to be serviced.
2471 s->request_suspend_timecode_transmission ();
2472 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2473 /* twiddle our thumbs */
2478 fake_locate (where);
2482 CursorDrag::motion (GdkEvent* event, bool)
2484 framepos_t const adjusted_frame = adjusted_current_frame (event);
2485 if (adjusted_frame != last_pointer_frame()) {
2486 fake_locate (adjusted_frame);
2491 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2493 _editor->_dragging_playhead = false;
2495 _cursor.track_canvas_item().ungrab();
2497 if (!movement_occurred && _stop) {
2501 motion (event, false);
2503 Session* s = _editor->session ();
2505 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2506 _editor->_pending_locate_request = true;
2507 s->request_resume_timecode_transmission ();
2512 CursorDrag::aborted (bool)
2514 _cursor.track_canvas_item().ungrab();
2516 if (_editor->_dragging_playhead) {
2517 _editor->session()->request_resume_timecode_transmission ();
2518 _editor->_dragging_playhead = false;
2521 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2524 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2525 : RegionDrag (e, i, p, v)
2527 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2531 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2533 Drag::start_grab (event, cursor);
2535 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2536 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2538 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2542 FadeInDrag::setup_pointer_frame_offset ()
2544 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2545 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2546 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2550 FadeInDrag::motion (GdkEvent* event, bool)
2552 framecnt_t fade_length;
2554 framepos_t const pos = adjusted_current_frame (event);
2556 boost::shared_ptr<Region> region = _primary->region ();
2558 if (pos < (region->position() + 64)) {
2559 fade_length = 64; // this should be a minimum defined somewhere
2560 } else if (pos > region->last_frame()) {
2561 fade_length = region->length();
2563 fade_length = pos - region->position();
2566 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2568 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2574 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2577 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2581 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2583 if (!movement_occurred) {
2587 framecnt_t fade_length;
2589 framepos_t const pos = adjusted_current_frame (event);
2591 boost::shared_ptr<Region> region = _primary->region ();
2593 if (pos < (region->position() + 64)) {
2594 fade_length = 64; // this should be a minimum defined somewhere
2595 } else if (pos > region->last_frame()) {
2596 fade_length = region->length();
2598 fade_length = pos - region->position();
2601 _editor->begin_reversible_command (_("change fade in length"));
2603 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2605 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2611 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2612 XMLNode &before = alist->get_state();
2614 tmp->audio_region()->set_fade_in_length (fade_length);
2615 tmp->audio_region()->set_fade_in_active (true);
2617 XMLNode &after = alist->get_state();
2618 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2621 _editor->commit_reversible_command ();
2625 FadeInDrag::aborted (bool)
2627 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2628 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2634 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2638 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2639 : RegionDrag (e, i, p, v)
2641 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2645 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2647 Drag::start_grab (event, cursor);
2649 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2650 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2652 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2656 FadeOutDrag::setup_pointer_frame_offset ()
2658 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2659 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2660 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2664 FadeOutDrag::motion (GdkEvent* event, bool)
2666 framecnt_t fade_length;
2668 framepos_t const pos = adjusted_current_frame (event);
2670 boost::shared_ptr<Region> region = _primary->region ();
2672 if (pos > (region->last_frame() - 64)) {
2673 fade_length = 64; // this should really be a minimum fade defined somewhere
2675 else if (pos < region->position()) {
2676 fade_length = region->length();
2679 fade_length = region->last_frame() - pos;
2682 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2684 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2690 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2693 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2697 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2699 if (!movement_occurred) {
2703 framecnt_t fade_length;
2705 framepos_t const pos = adjusted_current_frame (event);
2707 boost::shared_ptr<Region> region = _primary->region ();
2709 if (pos > (region->last_frame() - 64)) {
2710 fade_length = 64; // this should really be a minimum fade defined somewhere
2712 else if (pos < region->position()) {
2713 fade_length = region->length();
2716 fade_length = region->last_frame() - pos;
2719 _editor->begin_reversible_command (_("change fade out length"));
2721 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2723 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2729 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2730 XMLNode &before = alist->get_state();
2732 tmp->audio_region()->set_fade_out_length (fade_length);
2733 tmp->audio_region()->set_fade_out_active (true);
2735 XMLNode &after = alist->get_state();
2736 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2739 _editor->commit_reversible_command ();
2743 FadeOutDrag::aborted (bool)
2745 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2746 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2752 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2756 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2759 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2761 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2764 _points.push_back (ArdourCanvas::Duple (0, 0));
2765 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2768 MarkerDrag::~MarkerDrag ()
2770 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2775 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2777 location = new Location (*l);
2778 markers.push_back (m);
2783 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2785 Drag::start_grab (event, cursor);
2789 Location *location = _editor->find_location_from_marker (_marker, is_start);
2790 _editor->_dragging_edit_point = true;
2792 update_item (location);
2794 // _drag_line->show();
2795 // _line->raise_to_top();
2798 show_verbose_cursor_time (location->start());
2800 show_verbose_cursor_time (location->end());
2803 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2806 case Selection::Toggle:
2807 /* we toggle on the button release */
2809 case Selection::Set:
2810 if (!_editor->selection->selected (_marker)) {
2811 _editor->selection->set (_marker);
2814 case Selection::Extend:
2816 Locations::LocationList ll;
2817 list<Marker*> to_add;
2819 _editor->selection->markers.range (s, e);
2820 s = min (_marker->position(), s);
2821 e = max (_marker->position(), e);
2824 if (e < max_framepos) {
2827 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2828 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2829 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2832 to_add.push_back (lm->start);
2835 to_add.push_back (lm->end);
2839 if (!to_add.empty()) {
2840 _editor->selection->add (to_add);
2844 case Selection::Add:
2845 _editor->selection->add (_marker);
2849 /* Set up copies for us to manipulate during the drag
2852 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2854 Location* l = _editor->find_location_from_marker (*i, is_start);
2861 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2863 /* range: check that the other end of the range isn't
2866 CopiedLocationInfo::iterator x;
2867 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2868 if (*(*x).location == *l) {
2872 if (x == _copied_locations.end()) {
2873 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2875 (*x).markers.push_back (*i);
2876 (*x).move_both = true;
2884 MarkerDrag::setup_pointer_frame_offset ()
2887 Location *location = _editor->find_location_from_marker (_marker, is_start);
2888 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2892 MarkerDrag::motion (GdkEvent* event, bool)
2894 framecnt_t f_delta = 0;
2896 bool move_both = false;
2897 Location *real_location;
2898 Location *copy_location = 0;
2900 framepos_t const newframe = adjusted_current_frame (event);
2901 framepos_t next = newframe;
2903 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2907 CopiedLocationInfo::iterator x;
2909 /* find the marker we're dragging, and compute the delta */
2911 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2913 copy_location = (*x).location;
2915 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2917 /* this marker is represented by this
2918 * CopiedLocationMarkerInfo
2921 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
2926 if (real_location->is_mark()) {
2927 f_delta = newframe - copy_location->start();
2931 switch (_marker->type()) {
2932 case Marker::SessionStart:
2933 case Marker::RangeStart:
2934 case Marker::LoopStart:
2935 case Marker::PunchIn:
2936 f_delta = newframe - copy_location->start();
2939 case Marker::SessionEnd:
2940 case Marker::RangeEnd:
2941 case Marker::LoopEnd:
2942 case Marker::PunchOut:
2943 f_delta = newframe - copy_location->end();
2946 /* what kind of marker is this ? */
2955 if (x == _copied_locations.end()) {
2956 /* hmm, impossible - we didn't find the dragged marker */
2960 /* now move them all */
2962 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2964 copy_location = x->location;
2966 /* call this to find out if its the start or end */
2968 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
2972 if (real_location->locked()) {
2976 if (copy_location->is_mark()) {
2980 copy_location->set_start (copy_location->start() + f_delta);
2984 framepos_t new_start = copy_location->start() + f_delta;
2985 framepos_t new_end = copy_location->end() + f_delta;
2987 if (is_start) { // start-of-range marker
2989 if (move_both || (*x).move_both) {
2990 copy_location->set_start (new_start);
2991 copy_location->set_end (new_end);
2992 } else if (new_start < copy_location->end()) {
2993 copy_location->set_start (new_start);
2994 } else if (newframe > 0) {
2995 _editor->snap_to (next, 1, true);
2996 copy_location->set_end (next);
2997 copy_location->set_start (newframe);
3000 } else { // end marker
3002 if (move_both || (*x).move_both) {
3003 copy_location->set_end (new_end);
3004 copy_location->set_start (new_start);
3005 } else if (new_end > copy_location->start()) {
3006 copy_location->set_end (new_end);
3007 } else if (newframe > 0) {
3008 _editor->snap_to (next, -1, true);
3009 copy_location->set_start (next);
3010 copy_location->set_end (newframe);
3015 update_item (copy_location);
3017 /* now lookup the actual GUI items used to display this
3018 * location and move them to wherever the copy of the location
3019 * is now. This means that the logic in ARDOUR::Location is
3020 * still enforced, even though we are not (yet) modifying
3021 * the real Location itself.
3024 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3027 lm->set_position (copy_location->start(), copy_location->end());
3032 assert (!_copied_locations.empty());
3034 show_verbose_cursor_time (newframe);
3038 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3040 if (!movement_occurred) {
3042 if (was_double_click()) {
3043 _editor->rename_marker (_marker);
3047 /* just a click, do nothing but finish
3048 off the selection process
3051 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3054 case Selection::Set:
3055 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3056 _editor->selection->set (_marker);
3060 case Selection::Toggle:
3061 /* we toggle on the button release, click only */
3062 _editor->selection->toggle (_marker);
3065 case Selection::Extend:
3066 case Selection::Add:
3073 _editor->_dragging_edit_point = false;
3075 _editor->begin_reversible_command ( _("move marker") );
3076 XMLNode &before = _editor->session()->locations()->get_state();
3078 MarkerSelection::iterator i;
3079 CopiedLocationInfo::iterator x;
3082 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3083 x != _copied_locations.end() && i != _editor->selection->markers.end();
3086 Location * location = _editor->find_location_from_marker (*i, is_start);
3090 if (location->locked()) {
3094 if (location->is_mark()) {
3095 location->set_start (((*x).location)->start());
3097 location->set (((*x).location)->start(), ((*x).location)->end());
3102 XMLNode &after = _editor->session()->locations()->get_state();
3103 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3104 _editor->commit_reversible_command ();
3108 MarkerDrag::aborted (bool)
3114 MarkerDrag::update_item (Location*)
3119 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3121 _cumulative_x_drag (0),
3122 _cumulative_y_drag (0)
3124 if (_zero_gain_fraction < 0.0) {
3125 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3128 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3130 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3136 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3138 Drag::start_grab (event, _editor->cursors()->fader);
3140 // start the grab at the center of the control point so
3141 // the point doesn't 'jump' to the mouse after the first drag
3142 _fixed_grab_x = _point->get_x();
3143 _fixed_grab_y = _point->get_y();
3145 float const fraction = 1 - (_point->get_y() / _point->line().height());
3147 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3149 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3150 event->button.x + 10, event->button.y + 10);
3152 _editor->verbose_cursor()->show ();
3154 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3156 if (!_point->can_slide ()) {
3157 _x_constrained = true;
3162 ControlPointDrag::motion (GdkEvent* event, bool)
3164 double dx = _drags->current_pointer_x() - last_pointer_x();
3165 double dy = _drags->current_pointer_y() - last_pointer_y();
3167 if (event->button.state & Keyboard::SecondaryModifier) {
3172 /* coordinate in pixels relative to the start of the region (for region-based automation)
3173 or track (for track-based automation) */
3174 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3175 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3177 // calculate zero crossing point. back off by .01 to stay on the
3178 // positive side of zero
3179 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3181 // make sure we hit zero when passing through
3182 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3186 if (_x_constrained) {
3189 if (_y_constrained) {
3193 _cumulative_x_drag = cx - _fixed_grab_x;
3194 _cumulative_y_drag = cy - _fixed_grab_y;
3198 cy = min ((double) _point->line().height(), cy);
3200 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3202 if (!_x_constrained) {
3203 _editor->snap_to_with_modifier (cx_frames, event);
3206 cx_frames = min (cx_frames, _point->line().maximum_time());
3208 float const fraction = 1.0 - (cy / _point->line().height());
3210 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3212 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3216 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3218 if (!movement_occurred) {
3222 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3223 _editor->reset_point_selection ();
3227 motion (event, false);
3230 _point->line().end_drag (_pushing, _final_index);
3231 _editor->session()->commit_reversible_command ();
3235 ControlPointDrag::aborted (bool)
3237 _point->line().reset ();
3241 ControlPointDrag::active (Editing::MouseMode m)
3243 if (m == Editing::MouseGain) {
3244 /* always active in mouse gain */
3248 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3249 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3252 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3255 _cumulative_y_drag (0)
3257 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3261 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3263 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3266 _item = &_line->grab_item ();
3268 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3269 origin, and ditto for y.
3272 double cx = event->button.x;
3273 double cy = event->button.y;
3275 _line->parent_group().canvas_to_item (cx, cy);
3277 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3282 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3283 /* no adjacent points */
3287 Drag::start_grab (event, _editor->cursors()->fader);
3289 /* store grab start in parent frame */
3294 double fraction = 1.0 - (cy / _line->height());
3296 _line->start_drag_line (before, after, fraction);
3298 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3299 event->button.x + 10, event->button.y + 10);
3301 _editor->verbose_cursor()->show ();
3305 LineDrag::motion (GdkEvent* event, bool)
3307 double dy = _drags->current_pointer_y() - last_pointer_y();
3309 if (event->button.state & Keyboard::SecondaryModifier) {
3313 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3315 _cumulative_y_drag = cy - _fixed_grab_y;
3318 cy = min ((double) _line->height(), cy);
3320 double const fraction = 1.0 - (cy / _line->height());
3323 /* we are ignoring x position for this drag, so we can just pass in anything */
3324 _line->drag_motion (0, fraction, true, false, ignored);
3326 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3330 LineDrag::finished (GdkEvent* event, bool movement_occured)
3332 if (movement_occured) {
3333 motion (event, false);
3334 _line->end_drag (false, 0);
3336 /* add a new control point on the line */
3338 AutomationTimeAxisView* atv;
3340 _line->end_drag (false, 0);
3342 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3343 framepos_t where = _editor->window_event_sample (event, 0, 0);
3344 atv->add_automation_event (event, where, event->button.y, false);
3348 _editor->session()->commit_reversible_command ();
3352 LineDrag::aborted (bool)
3357 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3360 _cumulative_x_drag (0)
3362 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3366 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3368 Drag::start_grab (event);
3370 _line = reinterpret_cast<Line*> (_item);
3373 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3375 double cx = event->button.x;
3376 double cy = event->button.y;
3378 _item->parent()->canvas_to_item (cx, cy);
3380 /* store grab start in parent frame */
3381 _region_view_grab_x = cx;
3383 _before = *(float*) _item->get_data ("position");
3385 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3387 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3391 FeatureLineDrag::motion (GdkEvent*, bool)
3393 double dx = _drags->current_pointer_x() - last_pointer_x();
3395 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3397 _cumulative_x_drag += dx;
3399 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3408 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3410 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3412 float *pos = new float;
3415 _line->set_data ("position", pos);
3421 FeatureLineDrag::finished (GdkEvent*, bool)
3423 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3424 _arv->update_transient(_before, _before);
3428 FeatureLineDrag::aborted (bool)
3433 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3435 , _vertical_only (false)
3437 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3441 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3443 Drag::start_grab (event);
3444 show_verbose_cursor_time (adjusted_current_frame (event));
3448 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3455 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3457 framepos_t grab = grab_frame ();
3458 if (Config->get_rubberbanding_snaps_to_grid ()) {
3459 _editor->snap_to_with_modifier (grab, event);
3462 /* base start and end on initial click position */
3472 if (_drags->current_pointer_y() < grab_y()) {
3473 y1 = _drags->current_pointer_y();
3476 y2 = _drags->current_pointer_y();
3481 if (start != end || y1 != y2) {
3483 double x1 = _editor->sample_to_pixel (start);
3484 double x2 = _editor->sample_to_pixel (end);
3485 const double min_dimension = 2.0;
3487 _editor->rubberband_rect->set_x0 (x1);
3488 if (_vertical_only) {
3489 /* fixed 10 pixel width */
3490 _editor->rubberband_rect->set_x1 (x1 + 10);
3493 x2 = min (x1 - min_dimension, x2);
3495 x2 = max (x1 + min_dimension, x2);
3497 _editor->rubberband_rect->set_x1 (x2);
3500 _editor->rubberband_rect->set_y0 (y1);
3502 y2 = min (y1 - min_dimension, y2);
3504 y2 = max (y1 + min_dimension, y2);
3507 _editor->rubberband_rect->set_y1 (y2);
3509 _editor->rubberband_rect->show();
3510 _editor->rubberband_rect->raise_to_top();
3512 show_verbose_cursor_time (pf);
3514 do_select_things (event, true);
3519 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3524 if (grab_frame() < last_pointer_frame()) {
3526 x2 = last_pointer_frame ();
3529 x1 = last_pointer_frame ();
3535 if (_drags->current_pointer_y() < grab_y()) {
3536 y1 = _drags->current_pointer_y();
3539 y2 = _drags->current_pointer_y();
3543 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3547 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3549 if (movement_occurred) {
3551 motion (event, false);
3552 do_select_things (event, false);
3558 bool do_deselect = true;
3559 MidiTimeAxisView* mtv;
3561 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3563 if (_editor->selection->empty()) {
3564 /* nothing selected */
3565 add_midi_region (mtv);
3566 do_deselect = false;
3570 /* do not deselect if Primary or Tertiary (toggle-select or
3571 * extend-select are pressed.
3574 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3575 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3582 _editor->rubberband_rect->hide();
3586 RubberbandSelectDrag::aborted (bool)
3588 _editor->rubberband_rect->hide ();
3591 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3592 : RegionDrag (e, i, p, v)
3594 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3598 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3600 Drag::start_grab (event, cursor);
3602 show_verbose_cursor_time (adjusted_current_frame (event));
3606 TimeFXDrag::motion (GdkEvent* event, bool)
3608 RegionView* rv = _primary;
3609 StreamView* cv = rv->get_time_axis_view().view ();
3611 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3612 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3613 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3615 framepos_t const pf = adjusted_current_frame (event);
3617 if (pf > rv->region()->position()) {
3618 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3621 show_verbose_cursor_time (pf);
3625 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3627 _primary->get_time_axis_view().hide_timestretch ();
3629 if (!movement_occurred) {
3633 if (last_pointer_frame() < _primary->region()->position()) {
3634 /* backwards drag of the left edge - not usable */
3638 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3640 float percentage = (double) newlen / (double) _primary->region()->length();
3642 #ifndef USE_RUBBERBAND
3643 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3644 if (_primary->region()->data_type() == DataType::AUDIO) {
3645 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3649 if (!_editor->get_selection().regions.empty()) {
3650 /* primary will already be included in the selection, and edit
3651 group shared editing will propagate selection across
3652 equivalent regions, so just use the current region
3656 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3657 error << _("An error occurred while executing time stretch operation") << endmsg;
3663 TimeFXDrag::aborted (bool)
3665 _primary->get_time_axis_view().hide_timestretch ();
3668 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3671 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3675 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3677 Drag::start_grab (event);
3681 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3683 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3687 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3689 if (movement_occurred && _editor->session()) {
3690 /* make sure we stop */
3691 _editor->session()->request_transport_speed (0.0);
3696 ScrubDrag::aborted (bool)
3701 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3706 , _original_pointer_time_axis (-1)
3707 , _last_pointer_time_axis (-1)
3708 , _time_selection_at_start (!_editor->get_selection().time.empty())
3710 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3712 if (_time_selection_at_start) {
3713 start_at_start = _editor->get_selection().time.start();
3714 end_at_start = _editor->get_selection().time.end_frame();
3719 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3721 if (_editor->session() == 0) {
3725 Gdk::Cursor* cursor = 0;
3727 switch (_operation) {
3728 case CreateSelection:
3729 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3734 cursor = _editor->cursors()->selector;
3735 Drag::start_grab (event, cursor);
3738 case SelectionStartTrim:
3739 if (_editor->clicked_axisview) {
3740 _editor->clicked_axisview->order_selection_trims (_item, true);
3742 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3745 case SelectionEndTrim:
3746 if (_editor->clicked_axisview) {
3747 _editor->clicked_axisview->order_selection_trims (_item, false);
3749 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3753 Drag::start_grab (event, cursor);
3756 case SelectionExtend:
3757 Drag::start_grab (event, cursor);
3761 if (_operation == SelectionMove) {
3762 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3764 show_verbose_cursor_time (adjusted_current_frame (event));
3767 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3771 SelectionDrag::setup_pointer_frame_offset ()
3773 switch (_operation) {
3774 case CreateSelection:
3775 _pointer_frame_offset = 0;
3778 case SelectionStartTrim:
3780 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3783 case SelectionEndTrim:
3784 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3787 case SelectionExtend:
3793 SelectionDrag::motion (GdkEvent* event, bool first_move)
3795 framepos_t start = 0;
3797 framecnt_t length = 0;
3798 framecnt_t distance = 0;
3800 framepos_t const pending_position = adjusted_current_frame (event);
3802 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
3806 switch (_operation) {
3807 case CreateSelection:
3809 framepos_t grab = grab_frame ();
3812 grab = adjusted_current_frame (event, false);
3813 if (grab < pending_position) {
3814 _editor->snap_to (grab, -1);
3816 _editor->snap_to (grab, 1);
3820 if (pending_position < grab) {
3821 start = pending_position;
3824 end = pending_position;
3828 /* first drag: Either add to the selection
3829 or create a new selection
3836 /* adding to the selection */
3837 _editor->set_selected_track_as_side_effect (Selection::Add);
3838 _editor->clicked_selection = _editor->selection->add (start, end);
3845 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3846 _editor->set_selected_track_as_side_effect (Selection::Set);
3849 _editor->clicked_selection = _editor->selection->set (start, end);
3853 /* select all tracks within the rectangle that we've marked out so far */
3854 TrackViewList to_be_added_to_selection;
3855 TrackViewList to_be_removed_from_selection;
3856 TrackViewList& all_tracks (_editor->track_views);
3858 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
3860 if ((*i)->covered_by_y_range (grab_y(), _drags->current_pointer_y())) {
3861 if (!(*i)->get_selected()) {
3862 to_be_added_to_selection.push_back (*i);
3865 if ((*i)->get_selected()) {
3866 to_be_removed_from_selection.push_back (*i);
3871 if (!to_be_added_to_selection.empty()) {
3872 _editor->selection->add (to_be_added_to_selection);
3875 if (!to_be_removed_from_selection.empty()) {
3876 _editor->selection->remove (to_be_removed_from_selection);
3881 case SelectionStartTrim:
3883 start = _editor->selection->time[_editor->clicked_selection].start;
3884 end = _editor->selection->time[_editor->clicked_selection].end;
3886 if (pending_position > end) {
3889 start = pending_position;
3893 case SelectionEndTrim:
3895 start = _editor->selection->time[_editor->clicked_selection].start;
3896 end = _editor->selection->time[_editor->clicked_selection].end;
3898 if (pending_position < start) {
3901 end = pending_position;
3908 start = _editor->selection->time[_editor->clicked_selection].start;
3909 end = _editor->selection->time[_editor->clicked_selection].end;
3911 length = end - start;
3912 distance = pending_position - start;
3913 start = pending_position;
3914 _editor->snap_to (start);
3916 end = start + length;
3920 case SelectionExtend:
3924 _editor->maybe_autoscroll (true, false, false);
3927 switch (_operation) {
3929 if (_time_selection_at_start) {
3930 _editor->selection->move_time (distance);
3934 _editor->selection->replace (_editor->clicked_selection, start, end);
3938 if (_operation == SelectionMove) {
3939 show_verbose_cursor_time(start);
3941 show_verbose_cursor_time(pending_position);
3946 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3948 Session* s = _editor->session();
3950 if (movement_occurred) {
3951 motion (event, false);
3952 /* XXX this is not object-oriented programming at all. ick */
3953 if (_editor->selection->time.consolidate()) {
3954 _editor->selection->TimeChanged ();
3957 /* XXX what if its a music time selection? */
3959 if ( s->get_play_range() && s->transport_rolling() ) {
3960 s->request_play_range (&_editor->selection->time, true);
3962 if (Config->get_always_play_range() && !s->transport_rolling()) {
3963 s->request_locate (_editor->get_selection().time.start());
3969 /* just a click, no pointer movement.
3972 if (_operation == SelectionExtend) {
3973 if (_time_selection_at_start) {
3974 framepos_t pos = adjusted_current_frame (event, false);
3975 framepos_t start = min (pos, start_at_start);
3976 framepos_t end = max (pos, end_at_start);
3977 _editor->selection->set (start, end);
3980 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3981 if (_editor->clicked_selection) {
3982 _editor->selection->remove (_editor->clicked_selection);
3985 if (!_editor->clicked_selection) {
3986 _editor->selection->clear_time();
3991 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3992 _editor->selection->set (_editor->clicked_axisview);
3995 if (s && s->get_play_range () && s->transport_rolling()) {
3996 s->request_stop (false, false);
4001 _editor->stop_canvas_autoscroll ();
4002 _editor->clicked_selection = 0;
4006 SelectionDrag::aborted (bool)
4011 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4016 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4018 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4019 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4020 physical_screen_height (_editor->get_window())));
4021 _drag_rect->hide ();
4023 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4024 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4028 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4030 if (_editor->session() == 0) {
4034 Gdk::Cursor* cursor = 0;
4036 if (!_editor->temp_location) {
4037 _editor->temp_location = new Location (*_editor->session());
4040 switch (_operation) {
4041 case CreateRangeMarker:
4042 case CreateTransportMarker:
4043 case CreateCDMarker:
4045 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4050 cursor = _editor->cursors()->selector;
4054 Drag::start_grab (event, cursor);
4056 show_verbose_cursor_time (adjusted_current_frame (event));
4060 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4062 framepos_t start = 0;
4064 ArdourCanvas::Rectangle *crect;
4066 switch (_operation) {
4067 case CreateRangeMarker:
4068 crect = _editor->range_bar_drag_rect;
4070 case CreateTransportMarker:
4071 crect = _editor->transport_bar_drag_rect;
4073 case CreateCDMarker:
4074 crect = _editor->cd_marker_bar_drag_rect;
4077 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4082 framepos_t const pf = adjusted_current_frame (event);
4084 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4085 framepos_t grab = grab_frame ();
4086 _editor->snap_to (grab);
4088 if (pf < grab_frame()) {
4096 /* first drag: Either add to the selection
4097 or create a new selection.
4102 _editor->temp_location->set (start, end);
4106 update_item (_editor->temp_location);
4108 //_drag_rect->raise_to_top();
4113 _editor->maybe_autoscroll (true, false, false);
4116 _editor->temp_location->set (start, end);
4118 double x1 = _editor->sample_to_pixel (start);
4119 double x2 = _editor->sample_to_pixel (end);
4123 update_item (_editor->temp_location);
4126 show_verbose_cursor_time (pf);
4131 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4133 Location * newloc = 0;
4137 if (movement_occurred) {
4138 motion (event, false);
4141 switch (_operation) {
4142 case CreateRangeMarker:
4143 case CreateCDMarker:
4145 _editor->begin_reversible_command (_("new range marker"));
4146 XMLNode &before = _editor->session()->locations()->get_state();
4147 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4148 if (_operation == CreateCDMarker) {
4149 flags = Location::IsRangeMarker | Location::IsCDMarker;
4150 _editor->cd_marker_bar_drag_rect->hide();
4153 flags = Location::IsRangeMarker;
4154 _editor->range_bar_drag_rect->hide();
4156 newloc = new Location (
4157 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4160 _editor->session()->locations()->add (newloc, true);
4161 XMLNode &after = _editor->session()->locations()->get_state();
4162 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4163 _editor->commit_reversible_command ();
4167 case CreateTransportMarker:
4168 // popup menu to pick loop or punch
4169 _editor->new_transport_marker_context_menu (&event->button, _item);
4175 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4177 if (_operation == CreateTransportMarker) {
4179 /* didn't drag, so just locate */
4181 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4183 } else if (_operation == CreateCDMarker) {
4185 /* didn't drag, but mark is already created so do
4188 } else { /* operation == CreateRangeMarker */
4194 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4196 if (end == max_framepos) {
4197 end = _editor->session()->current_end_frame ();
4200 if (start == max_framepos) {
4201 start = _editor->session()->current_start_frame ();
4204 switch (_editor->mouse_mode) {
4206 /* find the two markers on either side and then make the selection from it */
4207 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4211 /* find the two markers on either side of the click and make the range out of it */
4212 _editor->selection->set (start, end);
4221 _editor->stop_canvas_autoscroll ();
4225 RangeMarkerBarDrag::aborted (bool)
4231 RangeMarkerBarDrag::update_item (Location* location)
4233 double const x1 = _editor->sample_to_pixel (location->start());
4234 double const x2 = _editor->sample_to_pixel (location->end());
4236 _drag_rect->set_x0 (x1);
4237 _drag_rect->set_x1 (x2);
4240 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4244 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4248 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4250 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4251 Drag::start_grab (event, _editor->cursors()->zoom_out);
4254 Drag::start_grab (event, _editor->cursors()->zoom_in);
4258 show_verbose_cursor_time (adjusted_current_frame (event));
4262 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4267 framepos_t const pf = adjusted_current_frame (event);
4269 framepos_t grab = grab_frame ();
4270 _editor->snap_to_with_modifier (grab, event);
4272 /* base start and end on initial click position */
4284 _editor->zoom_rect->show();
4285 _editor->zoom_rect->raise_to_top();
4288 _editor->reposition_zoom_rect(start, end);
4290 show_verbose_cursor_time (pf);
4295 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4297 if (movement_occurred) {
4298 motion (event, false);
4300 if (grab_frame() < last_pointer_frame()) {
4301 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4303 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4306 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4307 _editor->tav_zoom_step (_zoom_out);
4309 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4313 _editor->zoom_rect->hide();
4317 MouseZoomDrag::aborted (bool)
4319 _editor->zoom_rect->hide ();
4322 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4324 , _cumulative_dx (0)
4325 , _cumulative_dy (0)
4327 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4329 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4331 _region = &_primary->region_view ();
4332 _note_height = _region->midi_stream_view()->note_height ();
4336 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4338 Drag::start_grab (event);
4340 if (!(_was_selected = _primary->selected())) {
4342 /* tertiary-click means extend selection - we'll do that on button release,
4343 so don't add it here, because otherwise we make it hard to figure
4344 out the "extend-to" range.
4347 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4350 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4353 _region->note_selected (_primary, true);
4355 _region->unique_select (_primary);
4361 /** @return Current total drag x change in frames */
4363 NoteDrag::total_dx () const
4366 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4368 /* primary note time */
4369 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4371 /* new time of the primary note in session frames */
4372 frameoffset_t st = n + dx;
4374 framepos_t const rp = _region->region()->position ();
4376 /* prevent the note being dragged earlier than the region's position */
4379 /* snap and return corresponding delta */
4380 return _region->snap_frame_to_frame (st - rp) + rp - n;
4383 /** @return Current total drag y change in note number */
4385 NoteDrag::total_dy () const
4387 MidiStreamView* msv = _region->midi_stream_view ();
4388 double const y = _region->midi_view()->y_position ();
4389 /* new current note */
4390 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4392 n = max (msv->lowest_note(), n);
4393 n = min (msv->highest_note(), n);
4394 /* and work out delta */
4395 return n - msv->y_to_note (grab_y() - y);
4399 NoteDrag::motion (GdkEvent *, bool)
4401 /* Total change in x and y since the start of the drag */
4402 frameoffset_t const dx = total_dx ();
4403 int8_t const dy = total_dy ();
4405 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4406 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4407 double const tdy = -dy * _note_height - _cumulative_dy;
4410 _cumulative_dx += tdx;
4411 _cumulative_dy += tdy;
4413 int8_t note_delta = total_dy();
4415 _region->move_selection (tdx, tdy, note_delta);
4417 /* the new note value may be the same as the old one, but we
4418 * don't know what that means because the selection may have
4419 * involved more than one note and we might be doing something
4420 * odd with them. so show the note value anyway, always.
4424 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4426 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4427 (int) floor ((double)new_note));
4429 show_verbose_cursor_text (buf);
4434 NoteDrag::finished (GdkEvent* ev, bool moved)
4437 /* no motion - select note */
4439 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4440 _editor->current_mouse_mode() == Editing::MouseDraw) {
4442 if (_was_selected) {
4443 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4445 _region->note_deselected (_primary);
4448 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4449 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4451 if (!extend && !add && _region->selection_size() > 1) {
4452 _region->unique_select (_primary);
4453 } else if (extend) {
4454 _region->note_selected (_primary, true, true);
4456 /* it was added during button press */
4461 _region->note_dropped (_primary, total_dx(), total_dy());
4466 NoteDrag::aborted (bool)
4471 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4472 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4473 : Drag (editor, atv->base_item ())
4475 , _nothing_to_drag (false)
4477 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4478 y_origin = atv->y_position();
4479 setup (atv->lines ());
4482 /** Make an AutomationRangeDrag for region gain lines */
4483 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4484 : Drag (editor, rv->get_canvas_group ())
4486 , _nothing_to_drag (false)
4488 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4490 list<boost::shared_ptr<AutomationLine> > lines;
4491 lines.push_back (rv->get_gain_line ());
4492 y_origin = rv->get_time_axis_view().y_position();
4496 /** @param lines AutomationLines to drag.
4497 * @param offset Offset from the session start to the points in the AutomationLines.
4500 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4502 /* find the lines that overlap the ranges being dragged */
4503 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4504 while (i != lines.end ()) {
4505 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4508 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4510 /* check this range against all the AudioRanges that we are using */
4511 list<AudioRange>::const_iterator k = _ranges.begin ();
4512 while (k != _ranges.end()) {
4513 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4519 /* add it to our list if it overlaps at all */
4520 if (k != _ranges.end()) {
4525 _lines.push_back (n);
4531 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4535 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4537 return 1.0 - ((global_y - y_origin) / line->height());
4541 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4543 Drag::start_grab (event, cursor);
4545 /* Get line states before we start changing things */
4546 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4547 i->state = &i->line->get_state ();
4548 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4551 if (_ranges.empty()) {
4553 /* No selected time ranges: drag all points */
4554 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4555 uint32_t const N = i->line->npoints ();
4556 for (uint32_t j = 0; j < N; ++j) {
4557 i->points.push_back (i->line->nth (j));
4563 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4565 framecnt_t const half = (i->start + i->end) / 2;
4567 /* find the line that this audio range starts in */
4568 list<Line>::iterator j = _lines.begin();
4569 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4573 if (j != _lines.end()) {
4574 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4576 /* j is the line that this audio range starts in; fade into it;
4577 64 samples length plucked out of thin air.
4580 framepos_t a = i->start + 64;
4585 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4586 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4588 the_list->add (p, the_list->eval (p));
4589 the_list->add (q, the_list->eval (q));
4592 /* same thing for the end */
4595 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4599 if (j != _lines.end()) {
4600 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4602 /* j is the line that this audio range starts in; fade out of it;
4603 64 samples length plucked out of thin air.
4606 framepos_t b = i->end - 64;
4611 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4612 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4614 the_list->add (p, the_list->eval (p));
4615 the_list->add (q, the_list->eval (q));
4619 _nothing_to_drag = true;
4621 /* Find all the points that should be dragged and put them in the relevant
4622 points lists in the Line structs.
4625 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4627 uint32_t const N = i->line->npoints ();
4628 for (uint32_t j = 0; j < N; ++j) {
4630 /* here's a control point on this line */
4631 ControlPoint* p = i->line->nth (j);
4632 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4634 /* see if it's inside a range */
4635 list<AudioRange>::const_iterator k = _ranges.begin ();
4636 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4640 if (k != _ranges.end()) {
4641 /* dragging this point */
4642 _nothing_to_drag = false;
4643 i->points.push_back (p);
4649 if (_nothing_to_drag) {
4653 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4654 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4659 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4661 if (_nothing_to_drag) {
4665 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4666 float const f = y_fraction (l->line, _drags->current_pointer_y());
4667 /* we are ignoring x position for this drag, so we can just pass in anything */
4669 l->line->drag_motion (0, f, true, false, ignored);
4670 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4675 AutomationRangeDrag::finished (GdkEvent* event, bool)
4677 if (_nothing_to_drag) {
4681 motion (event, false);
4682 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4683 i->line->end_drag (false, 0);
4686 _editor->session()->commit_reversible_command ();
4690 AutomationRangeDrag::aborted (bool)
4692 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4697 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4700 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4701 layer = v->region()->layer ();
4702 initial_y = v->get_canvas_group()->position().y;
4703 initial_playlist = v->region()->playlist ();
4704 initial_position = v->region()->position ();
4705 initial_end = v->region()->position () + v->region()->length ();
4708 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4709 : Drag (e, i->canvas_item ())
4712 , _cumulative_dx (0)
4714 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4715 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4720 PatchChangeDrag::motion (GdkEvent* ev, bool)
4722 framepos_t f = adjusted_current_frame (ev);
4723 boost::shared_ptr<Region> r = _region_view->region ();
4724 f = max (f, r->position ());
4725 f = min (f, r->last_frame ());
4727 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4728 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4729 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4730 _cumulative_dx = dxu;
4734 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4736 if (!movement_occurred) {
4740 boost::shared_ptr<Region> r (_region_view->region ());
4741 framepos_t f = adjusted_current_frame (ev);
4742 f = max (f, r->position ());
4743 f = min (f, r->last_frame ());
4745 _region_view->move_patch_change (
4747 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4752 PatchChangeDrag::aborted (bool)
4754 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4758 PatchChangeDrag::setup_pointer_frame_offset ()
4760 boost::shared_ptr<Region> region = _region_view->region ();
4761 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4764 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4765 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4772 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4774 framepos_t const p = _region_view->region()->position ();
4775 double const y = _region_view->midi_view()->y_position ();
4777 x1 = max ((framepos_t) 0, x1 - p);
4778 x2 = max ((framepos_t) 0, x2 - p);
4779 y1 = max (0.0, y1 - y);
4780 y2 = max (0.0, y2 - y);
4782 _region_view->update_drag_selection (
4783 _editor->sample_to_pixel (x1),
4784 _editor->sample_to_pixel (x2),
4787 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4792 MidiRubberbandSelectDrag::deselect_things ()
4797 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4798 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4801 _vertical_only = true;
4805 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4807 double const y = _region_view->midi_view()->y_position ();
4809 y1 = max (0.0, y1 - y);
4810 y2 = max (0.0, y2 - y);
4812 _region_view->update_vertical_drag_selection (
4815 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4820 MidiVerticalSelectDrag::deselect_things ()
4825 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4826 : RubberbandSelectDrag (e, i)
4832 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4834 if (drag_in_progress) {
4835 /* We just want to select things at the end of the drag, not during it */
4839 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4841 _editor->begin_reversible_command (_("rubberband selection"));
4842 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4843 _editor->commit_reversible_command ();
4847 EditorRubberbandSelectDrag::deselect_things ()
4849 if (!getenv("ARDOUR_SAE")) {
4850 _editor->selection->clear_tracks();
4852 _editor->selection->clear_regions();
4853 _editor->selection->clear_points ();
4854 _editor->selection->clear_lines ();
4857 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4865 NoteCreateDrag::~NoteCreateDrag ()
4871 NoteCreateDrag::grid_frames (framepos_t t) const
4874 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4879 return _region_view->region_beats_to_region_frames (grid_beats);
4883 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4885 Drag::start_grab (event, cursor);
4887 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4889 framepos_t pf = _drags->current_pointer_frame ();
4890 framecnt_t const g = grid_frames (pf);
4892 /* Hack so that we always snap to the note that we are over, instead of snapping
4893 to the next one if we're more than halfway through the one we're over.
4895 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4899 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4901 MidiStreamView* sv = _region_view->midi_stream_view ();
4902 double const x = _editor->sample_to_pixel (_note[0]);
4903 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4905 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4906 _drag_rect->set_outline_all ();
4907 _drag_rect->set_outline_color (0xffffff99);
4908 _drag_rect->set_fill_color (0xffffff66);
4912 NoteCreateDrag::motion (GdkEvent* event, bool)
4914 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4915 double const x = _editor->sample_to_pixel (_note[1]);
4916 if (_note[1] > _note[0]) {
4917 _drag_rect->set_x1 (x);
4919 _drag_rect->set_x0 (x);
4924 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4926 if (!had_movement) {
4930 framepos_t const start = min (_note[0], _note[1]);
4931 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
4933 framecnt_t const g = grid_frames (start);
4934 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4936 if (_editor->snap_mode() == SnapNormal && length < g) {
4937 length = g - one_tick;
4940 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4942 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
4946 NoteCreateDrag::y_to_region (double y) const
4949 _region_view->get_canvas_group()->canvas_to_item (x, y);
4954 NoteCreateDrag::aborted (bool)
4959 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4964 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
4968 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4970 Drag::start_grab (event, cursor);
4974 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4980 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4983 distance = _drags->current_pointer_x() - grab_x();
4984 len = ar->fade_in()->back()->when;
4986 distance = grab_x() - _drags->current_pointer_x();
4987 len = ar->fade_out()->back()->when;
4990 /* how long should it be ? */
4992 new_length = len + _editor->pixel_to_sample (distance);
4994 /* now check with the region that this is legal */
4996 new_length = ar->verify_xfade_bounds (new_length, start);
4999 arv->reset_fade_in_shape_width (ar, new_length);
5001 arv->reset_fade_out_shape_width (ar, new_length);
5006 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5012 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5015 distance = _drags->current_pointer_x() - grab_x();
5016 len = ar->fade_in()->back()->when;
5018 distance = grab_x() - _drags->current_pointer_x();
5019 len = ar->fade_out()->back()->when;
5022 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5024 _editor->begin_reversible_command ("xfade trim");
5025 ar->playlist()->clear_owned_changes ();
5028 ar->set_fade_in_length (new_length);
5030 ar->set_fade_out_length (new_length);
5033 /* Adjusting the xfade may affect other regions in the playlist, so we need
5034 to get undo Commands from the whole playlist rather than just the
5038 vector<Command*> cmds;
5039 ar->playlist()->rdiff (cmds);
5040 _editor->session()->add_commands (cmds);
5041 _editor->commit_reversible_command ();
5046 CrossfadeEdgeDrag::aborted (bool)
5049 arv->redraw_start_xfade ();
5051 arv->redraw_end_xfade ();