2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/dB.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/operations.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/session.h"
44 #include "audio_region_view.h"
45 #include "midi_region_view.h"
46 #include "ardour_ui.h"
47 #include "gui_thread.h"
48 #include "control_point.h"
50 #include "region_gain_line.h"
51 #include "editor_drag.h"
52 #include "audio_time_axis.h"
53 #include "midi_time_axis.h"
54 #include "selection.h"
55 #include "midi_selection.h"
56 #include "automation_time_axis.h"
58 #include "editor_cursors.h"
59 #include "mouse_cursors.h"
60 #include "note_base.h"
61 #include "patch_change.h"
62 #include "verbose_cursor.h"
65 using namespace ARDOUR;
68 using namespace Gtkmm2ext;
69 using namespace Editing;
70 using namespace ArdourCanvas;
72 using Gtkmm2ext::Keyboard;
74 double ControlPointDrag::_zero_gain_fraction = -1.0;
76 DragManager::DragManager (Editor* e)
79 , _current_pointer_frame (0)
83 DragManager::~DragManager ()
88 /** Call abort for each active drag */
94 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
99 if (!_drags.empty ()) {
100 _editor->set_follow_playhead (_old_follow_playhead, false);
109 DragManager::add (Drag* d)
111 d->set_manager (this);
112 _drags.push_back (d);
116 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
118 d->set_manager (this);
119 _drags.push_back (d);
124 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
126 /* Prevent follow playhead during the drag to be nice to the user */
127 _old_follow_playhead = _editor->follow_playhead ();
128 _editor->set_follow_playhead (false);
130 _current_pointer_frame = _editor->canvas_event_frame (e, &_current_pointer_x, &_current_pointer_y);
132 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
133 (*i)->start_grab (e, c);
137 /** Call end_grab for each active drag.
138 * @return true if any drag reported movement having occurred.
141 DragManager::end_grab (GdkEvent* e)
146 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
147 bool const t = (*i)->end_grab (e);
158 _editor->set_follow_playhead (_old_follow_playhead, false);
164 DragManager::mark_double_click ()
166 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
167 (*i)->set_double_click (true);
172 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
176 _current_pointer_frame = _editor->canvas_event_frame (e, &_current_pointer_x, &_current_pointer_y);
178 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
179 bool const t = (*i)->motion_handler (e, from_autoscroll);
190 DragManager::window_motion_handler (GdkEvent* e, bool from_autoscroll)
194 _current_pointer_frame = _editor->canvas_event_frame (e, &_current_pointer_x, &_current_pointer_y);
196 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
197 bool const t = (*i)->motion_handler (e, from_autoscroll);
208 DragManager::have_item (ArdourCanvas::Item* i) const
210 list<Drag*>::const_iterator j = _drags.begin ();
211 while (j != _drags.end() && (*j)->item () != i) {
215 return j != _drags.end ();
218 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
221 , _pointer_frame_offset (0)
222 , _move_threshold_passed (false)
223 , _was_double_click (false)
224 , _raw_grab_frame (0)
226 , _last_pointer_frame (0)
232 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
245 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
247 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
249 if (Keyboard::is_button2_event (&event->button)) {
250 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
251 _y_constrained = true;
252 _x_constrained = false;
254 _y_constrained = false;
255 _x_constrained = true;
258 _x_constrained = false;
259 _y_constrained = false;
262 _raw_grab_frame = _editor->canvas_event_frame (event, &_grab_x, &_grab_y);
263 setup_pointer_frame_offset ();
264 _grab_frame = adjusted_frame (_raw_grab_frame, event);
265 _last_pointer_frame = _grab_frame;
266 _last_pointer_x = _grab_x;
267 _last_pointer_y = _grab_y;
273 /* CAIROCANVAS need a variant here that passes *cursor */
278 if (_editor->session() && _editor->session()->transport_rolling()) {
281 _was_rolling = false;
284 switch (_editor->snap_type()) {
285 case SnapToRegionStart:
286 case SnapToRegionEnd:
287 case SnapToRegionSync:
288 case SnapToRegionBoundary:
289 _editor->build_region_boundary_cache ();
296 /** Call to end a drag `successfully'. Ungrabs item and calls
297 * subclass' finished() method.
299 * @param event GDK event, or 0.
300 * @return true if some movement occurred, otherwise false.
303 Drag::end_grab (GdkEvent* event)
305 _editor->stop_canvas_autoscroll ();
309 finished (event, _move_threshold_passed);
311 _editor->verbose_cursor()->hide ();
313 return _move_threshold_passed;
317 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
321 if (f > _pointer_frame_offset) {
322 pos = f - _pointer_frame_offset;
326 _editor->snap_to_with_modifier (pos, event);
333 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
335 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
339 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
341 /* check to see if we have moved in any way that matters since the last motion event */
342 if (_move_threshold_passed &&
343 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
344 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
348 pair<framecnt_t, int> const threshold = move_threshold ();
350 bool const old_move_threshold_passed = _move_threshold_passed;
352 if (!from_autoscroll && !_move_threshold_passed) {
354 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
355 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
357 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
360 if (active (_editor->mouse_mode) && _move_threshold_passed) {
362 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
363 if (!from_autoscroll) {
364 bool const moving_left = _drags->current_pointer_x() < _last_pointer_x;
365 bool const moving_up = _drags->current_pointer_y() < _last_pointer_y;
366 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), moving_left, moving_up);
369 motion (event, _move_threshold_passed != old_move_threshold_passed);
371 _last_pointer_x = _drags->current_pointer_x ();
372 _last_pointer_y = _drags->current_pointer_y ();
373 _last_pointer_frame = adjusted_current_frame (event);
381 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
389 aborted (_move_threshold_passed);
391 _editor->stop_canvas_autoscroll ();
392 _editor->verbose_cursor()->hide ();
396 Drag::show_verbose_cursor_time (framepos_t frame)
398 _editor->verbose_cursor()->set_time (
400 _drags->current_pointer_x() + 10,
401 _drags->current_pointer_y() + 10
404 _editor->verbose_cursor()->show ();
408 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
410 _editor->verbose_cursor()->show (xoffset);
412 _editor->verbose_cursor()->set_duration (
414 _drags->current_pointer_x() + 10,
415 _drags->current_pointer_y() + 10
420 Drag::show_verbose_cursor_text (string const & text)
422 _editor->verbose_cursor()->show ();
424 _editor->verbose_cursor()->set (
426 _drags->current_pointer_x() + 10,
427 _drags->current_pointer_y() + 10
431 boost::shared_ptr<Region>
432 Drag::add_midi_region (MidiTimeAxisView* view)
434 if (_editor->session()) {
435 const TempoMap& map (_editor->session()->tempo_map());
436 framecnt_t pos = grab_frame();
437 const Meter& m = map.meter_at (pos);
438 /* not that the frame rate used here can be affected by pull up/down which
441 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
442 return view->add_region (grab_frame(), len, true);
445 return boost::shared_ptr<Region>();
448 struct EditorOrderTimeAxisViewSorter {
449 bool operator() (TimeAxisView* a, TimeAxisView* b) {
450 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
451 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
453 return ra->route()->order_key () < rb->route()->order_key ();
457 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
461 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
463 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
464 as some of the regions we are dragging may be on such tracks.
467 TrackViewList track_views = _editor->track_views;
468 track_views.sort (EditorOrderTimeAxisViewSorter ());
470 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
471 _time_axis_views.push_back (*i);
473 TimeAxisView::Children children_list = (*i)->get_child_list ();
474 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
475 _time_axis_views.push_back (j->get());
479 /* the list of views can be empty at this point if this is a region list-insert drag
482 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
483 _views.push_back (DraggingView (*i, this));
486 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
490 RegionDrag::region_going_away (RegionView* v)
492 list<DraggingView>::iterator i = _views.begin ();
493 while (i != _views.end() && i->view != v) {
497 if (i != _views.end()) {
502 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
503 * or -1 if it is not found.
506 RegionDrag::find_time_axis_view (TimeAxisView* t) const
509 int const N = _time_axis_views.size ();
510 while (i < N && _time_axis_views[i] != t) {
521 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
522 : RegionDrag (e, i, p, v)
525 , _last_pointer_time_axis_view (0)
526 , _last_pointer_layer (0)
528 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
532 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
534 Drag::start_grab (event, cursor);
536 show_verbose_cursor_time (_last_frame_position);
538 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
540 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
541 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
546 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
548 /* compute the amount of pointer motion in frames, and where
549 the region would be if we moved it by that much.
551 *pending_region_position = adjusted_current_frame (event);
553 framepos_t sync_frame;
554 framecnt_t sync_offset;
557 sync_offset = _primary->region()->sync_offset (sync_dir);
559 /* we don't handle a sync point that lies before zero.
561 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
563 sync_frame = *pending_region_position + (sync_dir*sync_offset);
565 _editor->snap_to_with_modifier (sync_frame, event);
567 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
570 *pending_region_position = _last_frame_position;
573 if (*pending_region_position > max_framepos - _primary->region()->length()) {
574 *pending_region_position = _last_frame_position;
579 /* in locked edit mode, reverse the usual meaning of _x_constrained */
580 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
582 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
584 /* x movement since last time (in pixels) */
585 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
587 /* total x movement */
588 framecnt_t total_dx = *pending_region_position;
589 if (regions_came_from_canvas()) {
590 total_dx = total_dx - grab_frame ();
593 /* check that no regions have gone off the start of the session */
594 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
595 if ((i->view->region()->position() + total_dx) < 0) {
597 *pending_region_position = _last_frame_position;
602 _last_frame_position = *pending_region_position;
609 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
611 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
612 int const n = i->time_axis_view + delta_track;
613 if (n < 0 || n >= int (_time_axis_views.size())) {
614 /* off the top or bottom track */
618 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
619 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
620 /* not a track, or the wrong type */
624 double const l = i->layer + delta_layer;
626 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
627 mode to allow the user to place a region below another on layer 0.
629 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
630 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
631 If it has, the layers will be munged later anyway, so it's ok.
637 /* all regions being dragged are ok with this change */
642 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
644 assert (!_views.empty ());
646 /* Find the TimeAxisView that the pointer is now over */
647 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
649 /* Bail early if we're not over a track */
650 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
652 if (!rtv || !rtv->is_track()) {
653 _editor->verbose_cursor()->hide ();
657 if (first_move && tv.first->view()->layer_display() == Stacked) {
658 tv.first->view()->set_layer_display (Expanded);
661 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
663 /* Here's the current pointer position in terms of time axis view and layer */
664 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
665 double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
667 /* Work out the change in x */
668 framepos_t pending_region_position;
669 double const x_delta = compute_x_delta (event, &pending_region_position);
671 /* Work out the change in y */
673 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
674 double delta_layer = current_pointer_layer - _last_pointer_layer;
676 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
677 /* this y movement is not allowed, so do no y movement this time */
678 delta_time_axis_view = 0;
682 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
683 /* haven't reached next snap point, and we're not switching
684 trackviews nor layers. nothing to do.
689 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
691 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
693 RegionView* rv = i->view;
695 if (rv->region()->locked() || rv->region()->video_locked()) {
703 /* Reparent to a non scrolling group so that we can keep the
704 region selection above all time axis views.
705 Reparenting means that we will have to move the region view
706 within its new parent, as the two parent groups have different coordinates.
709 ArdourCanvas::Group* rvg = rv->get_canvas_group();
710 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
712 rv->get_canvas_group()->reparent (_editor->_region_motion_group);
714 rv->fake_set_opaque (true);
715 rvg->set_position (rv_canvas_offset);
718 /* If we have moved tracks, we'll fudge the layer delta so that the
719 region gets moved back onto layer 0 on its new track; this avoids
720 confusion when dragging regions from non-zero layers onto different
723 double this_delta_layer = delta_layer;
724 if (delta_time_axis_view != 0) {
725 this_delta_layer = - i->layer;
728 /* The TimeAxisView that this region is now on */
729 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
731 /* Ensure it is moved from stacked -> expanded if appropriate */
732 if (tv->view()->layer_display() == Stacked) {
733 tv->view()->set_layer_display (Expanded);
736 /* We're only allowed to go -ve in layer on Expanded views */
737 if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
738 this_delta_layer = - i->layer;
742 rv->set_height (tv->view()->child_height ());
744 /* Update show/hidden status as the region view may have come from a hidden track,
745 or have moved to one.
748 rv->get_canvas_group()->hide ();
750 rv->get_canvas_group()->show ();
753 /* Update the DraggingView */
754 i->time_axis_view += delta_time_axis_view;
755 i->layer += this_delta_layer;
758 _editor->mouse_brush_insert_region (rv, pending_region_position);
763 /* Get the y coordinate of the top of the track that this region is now on */
764 tv->canvas_display()->item_to_canvas (x, y);
766 /* And adjust for the layer that it should be on */
767 StreamView* cv = tv->view ();
768 switch (cv->layer_display ()) {
772 y += (cv->layers() - i->layer - 1) * cv->child_height ();
775 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
779 /* Now move the region view */
780 rv->move (x_delta, y - rv->get_canvas_group()->position().y);
783 } /* foreach region */
785 _total_x_delta += x_delta;
787 if (x_delta != 0 && !_brushing) {
788 show_verbose_cursor_time (_last_frame_position);
791 _last_pointer_time_axis_view += delta_time_axis_view;
792 _last_pointer_layer += delta_layer;
796 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
798 if (_copy && first_move) {
800 /* duplicate the regionview(s) and region(s) */
802 list<DraggingView> new_regionviews;
804 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
806 RegionView* rv = i->view;
807 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
808 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
810 const boost::shared_ptr<const Region> original = rv->region();
811 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
812 region_copy->set_position (original->position());
816 boost::shared_ptr<AudioRegion> audioregion_copy
817 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
819 nrv = new AudioRegionView (*arv, audioregion_copy);
821 boost::shared_ptr<MidiRegion> midiregion_copy
822 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
823 nrv = new MidiRegionView (*mrv, midiregion_copy);
828 nrv->get_canvas_group()->show ();
829 new_regionviews.push_back (DraggingView (nrv, this));
831 /* swap _primary to the copy */
833 if (rv == _primary) {
837 /* ..and deselect the one we copied */
839 rv->set_selected (false);
842 if (!new_regionviews.empty()) {
844 /* reflect the fact that we are dragging the copies */
846 _views = new_regionviews;
848 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
852 RegionMotionDrag::motion (event, first_move);
856 RegionMotionDrag::finished (GdkEvent *, bool)
858 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
863 if ((*i)->view()->layer_display() == Expanded) {
864 (*i)->view()->set_layer_display (Stacked);
870 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
872 RegionMotionDrag::finished (ev, movement_occurred);
874 if (!movement_occurred) {
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 rv->region()->clear_changes ();
1083 motion on the same track. plonk the previously reparented region
1084 back to its original canvas group (its streamview).
1085 No need to do anything for copies as they are fake regions which will be deleted.
1088 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1089 rv->get_canvas_group()->set_y_position (i->initial_y);
1092 /* just change the model */
1094 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1096 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1097 playlist->set_layer (rv->region(), dest_layer);
1100 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1102 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1105 playlist->freeze ();
1108 /* this movement may result in a crossfade being modified, so we need to get undo
1109 data from the playlist as well as the region.
1112 r = modified_playlists.insert (playlist);
1114 playlist->clear_changes ();
1117 rv->region()->set_position (where);
1119 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1122 if (changed_tracks) {
1124 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1125 was selected in all of them, then removing it from a playlist will have removed all
1126 trace of it from _views (i.e. there were N regions selected, we removed 1,
1127 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1128 corresponding regionview, and _views is now empty).
1130 This could have invalidated any and all iterators into _views.
1132 The heuristic we use here is: if the region selection is empty, break out of the loop
1133 here. if the region selection is not empty, then restart the loop because we know that
1134 we must have removed at least the region(view) we've just been working on as well as any
1135 that we processed on previous iterations.
1137 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1138 we can just iterate.
1142 if (_views.empty()) {
1153 /* If we've created new regions either by copying or moving
1154 to a new track, we want to replace the old selection with the new ones
1157 if (new_views.size() > 0) {
1158 _editor->selection->set (new_views);
1161 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1165 /* write commands for the accumulated diffs for all our modified playlists */
1166 add_stateful_diff_commands_for_playlists (modified_playlists);
1168 _editor->commit_reversible_command ();
1170 /* We have futzed with the layering of canvas items on our streamviews.
1171 If any region changed layer, this will have resulted in the stream
1172 views being asked to set up their region views, and all will be well.
1173 If not, we might now have badly-ordered region views. Ask the StreamViews
1174 involved to sort themselves out, just in case.
1177 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1178 (*i)->view()->playlist_layered ((*i)->track ());
1182 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1183 * @param region Region to remove.
1184 * @param playlist playlist To remove from.
1185 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1186 * that clear_changes () is only called once per playlist.
1189 RegionMoveDrag::remove_region_from_playlist (
1190 boost::shared_ptr<Region> region,
1191 boost::shared_ptr<Playlist> playlist,
1192 PlaylistSet& modified_playlists
1195 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1198 playlist->clear_changes ();
1201 playlist->remove_region (region);
1205 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1206 * clearing the playlist's diff history first if necessary.
1207 * @param region Region to insert.
1208 * @param dest_rtv Destination RouteTimeAxisView.
1209 * @param dest_layer Destination layer.
1210 * @param where Destination position.
1211 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1212 * that clear_changes () is only called once per playlist.
1213 * @return New RegionView, or 0 if no insert was performed.
1216 RegionMoveDrag::insert_region_into_playlist (
1217 boost::shared_ptr<Region> region,
1218 RouteTimeAxisView* dest_rtv,
1221 PlaylistSet& modified_playlists
1224 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1225 if (!dest_playlist) {
1229 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1230 _new_region_view = 0;
1231 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1233 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1234 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1236 dest_playlist->clear_changes ();
1239 dest_playlist->add_region (region, where);
1241 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1242 dest_playlist->set_layer (region, dest_layer);
1247 assert (_new_region_view);
1249 return _new_region_view;
1253 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1255 _new_region_view = rv;
1259 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1261 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1262 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1264 _editor->session()->add_command (c);
1273 RegionMoveDrag::aborted (bool movement_occurred)
1277 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1284 RegionMotionDrag::aborted (movement_occurred);
1289 RegionMotionDrag::aborted (bool)
1291 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1293 StreamView* sview = (*i)->view();
1296 if (sview->layer_display() == Expanded) {
1297 sview->set_layer_display (Stacked);
1302 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1303 RegionView* rv = i->view;
1304 TimeAxisView* tv = &(rv->get_time_axis_view ());
1305 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1307 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1308 rv->get_canvas_group()->set_y_position (0);
1310 rv->fake_set_opaque (false);
1311 rv->move (-_total_x_delta, 0);
1312 rv->set_height (rtv->view()->child_height ());
1316 /** @param b true to brush, otherwise false.
1317 * @param c true to make copies of the regions being moved, otherwise false.
1319 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1320 : RegionMotionDrag (e, i, p, v, b),
1323 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1326 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1327 if (rtv && rtv->is_track()) {
1328 speed = rtv->track()->speed ();
1331 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1335 RegionMoveDrag::setup_pointer_frame_offset ()
1337 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1340 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1341 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1343 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1345 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1346 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1348 _primary = v->view()->create_region_view (r, false, false);
1350 _primary->get_canvas_group()->show ();
1351 _primary->set_position (pos, 0);
1352 _views.push_back (DraggingView (_primary, this));
1354 _last_frame_position = pos;
1356 _item = _primary->get_canvas_group ();
1360 RegionInsertDrag::finished (GdkEvent *, bool)
1362 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1364 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1365 _primary->get_canvas_group()->set_y_position (0);
1367 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1369 _editor->begin_reversible_command (Operations::insert_region);
1370 playlist->clear_changes ();
1371 playlist->add_region (_primary->region (), _last_frame_position);
1372 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1373 _editor->commit_reversible_command ();
1381 RegionInsertDrag::aborted (bool)
1388 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1389 : RegionMoveDrag (e, i, p, v, false, false)
1391 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1394 struct RegionSelectionByPosition {
1395 bool operator() (RegionView*a, RegionView* b) {
1396 return a->region()->position () < b->region()->position();
1401 RegionSpliceDrag::motion (GdkEvent* event, bool)
1403 /* Which trackview is this ? */
1405 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1406 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1408 /* The region motion is only processed if the pointer is over
1412 if (!tv || !tv->is_track()) {
1413 /* To make sure we hide the verbose canvas cursor when the mouse is
1414 not held over and audiotrack.
1416 _editor->verbose_cursor()->hide ();
1422 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1428 RegionSelection copy (_editor->selection->regions);
1430 RegionSelectionByPosition cmp;
1433 framepos_t const pf = adjusted_current_frame (event);
1435 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1437 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1443 boost::shared_ptr<Playlist> playlist;
1445 if ((playlist = atv->playlist()) == 0) {
1449 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1454 if (pf < (*i)->region()->last_frame() + 1) {
1458 if (pf > (*i)->region()->first_frame()) {
1464 playlist->shuffle ((*i)->region(), dir);
1469 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1471 RegionMoveDrag::finished (event, movement_occurred);
1475 RegionSpliceDrag::aborted (bool)
1480 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1482 _view (dynamic_cast<MidiTimeAxisView*> (v))
1484 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1490 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1493 _region = add_midi_region (_view);
1494 _view->playlist()->freeze ();
1497 framepos_t const f = adjusted_current_frame (event);
1498 if (f < grab_frame()) {
1499 _region->set_position (f);
1502 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1503 so that if this region is duplicated, its duplicate starts on
1504 a snap point rather than 1 frame after a snap point. Otherwise things get
1505 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1506 place snapped notes at the start of the region.
1509 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1510 _region->set_length (len < 1 ? 1 : len);
1516 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1518 if (!movement_occurred) {
1519 add_midi_region (_view);
1521 _view->playlist()->thaw ();
1526 RegionCreateDrag::aborted (bool)
1529 _view->playlist()->thaw ();
1535 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1539 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1543 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1545 Gdk::Cursor* cursor;
1546 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1548 float x_fraction = cnote->mouse_x_fraction ();
1550 if (x_fraction > 0.0 && x_fraction < 0.25) {
1551 cursor = _editor->cursors()->left_side_trim;
1553 cursor = _editor->cursors()->right_side_trim;
1556 Drag::start_grab (event, cursor);
1558 region = &cnote->region_view();
1560 double const region_start = region->get_position_pixels();
1561 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1563 if (grab_x() <= middle_point) {
1564 cursor = _editor->cursors()->left_side_trim;
1567 cursor = _editor->cursors()->right_side_trim;
1573 if (event->motion.state & Keyboard::PrimaryModifier) {
1579 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1581 if (ms.size() > 1) {
1582 /* has to be relative, may make no sense otherwise */
1586 /* select this note; if it is already selected, preserve the existing selection,
1587 otherwise make this note the only one selected.
1589 region->note_selected (cnote, cnote->selected ());
1591 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1592 MidiRegionSelection::iterator next;
1595 (*r)->begin_resizing (at_front);
1601 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1603 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1604 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1605 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1607 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1612 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1614 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1615 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1616 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1618 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1623 NoteResizeDrag::aborted (bool)
1625 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1626 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1627 (*r)->abort_resizing ();
1631 AVDraggingView::AVDraggingView (RegionView* v)
1634 initial_position = v->region()->position ();
1637 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1640 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1643 TrackViewList empty;
1645 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1646 std::list<RegionView*> views = rs.by_layer();
1648 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1649 RegionView* rv = (*i);
1650 if (!rv->region()->video_locked()) {
1653 _views.push_back (AVDraggingView (rv));
1658 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1660 Drag::start_grab (event);
1661 if (_editor->session() == 0) {
1665 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1666 _max_backwards_drag = (
1667 ARDOUR_UI::instance()->video_timeline->get_duration()
1668 + ARDOUR_UI::instance()->video_timeline->get_offset()
1669 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1672 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1673 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1674 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1677 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1680 Timecode::Time timecode;
1681 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1682 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);
1683 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1684 _editor->verbose_cursor()->show ();
1688 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1690 if (_editor->session() == 0) {
1693 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1697 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1698 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
1700 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1701 dt = - _max_backwards_drag;
1704 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1705 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1707 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1708 RegionView* rv = i->view;
1709 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1712 rv->fake_set_opaque (true);
1713 rv->region()->clear_changes ();
1714 rv->region()->suspend_property_changes();
1716 rv->region()->set_position(i->initial_position + dt);
1717 rv->region_changed(ARDOUR::Properties::position);
1720 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1721 Timecode::Time timecode;
1722 Timecode::Time timediff;
1724 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1725 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1726 snprintf (buf, sizeof (buf),
1727 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1728 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1729 , _("Video Start:"),
1730 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1732 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1734 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1735 _editor->verbose_cursor()->show ();
1739 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1741 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1745 if (!movement_occurred || ! _editor->session()) {
1749 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1751 _editor->begin_reversible_command (_("Move Video"));
1753 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1754 ARDOUR_UI::instance()->video_timeline->save_undo();
1755 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1756 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1758 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1759 i->view->drag_end();
1760 i->view->fake_set_opaque (false);
1761 i->view->region()->resume_property_changes ();
1763 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1766 _editor->session()->maybe_update_session_range(
1767 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1768 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1772 _editor->commit_reversible_command ();
1776 VideoTimeLineDrag::aborted (bool)
1778 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1781 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1782 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1784 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1785 i->view->region()->resume_property_changes ();
1786 i->view->region()->set_position(i->initial_position);
1790 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1791 : RegionDrag (e, i, p, v)
1793 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1794 _preserve_fade_anchor = preserve_fade_anchor;
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 switch (_operation) {
1833 show_verbose_cursor_time (region_start);
1834 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1835 i->view->trim_front_starting ();
1839 show_verbose_cursor_time (region_end);
1842 show_verbose_cursor_time (pf);
1846 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1847 i->view->region()->suspend_property_changes ();
1852 TrimDrag::motion (GdkEvent* event, bool first_move)
1854 RegionView* rv = _primary;
1857 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1858 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1859 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1860 frameoffset_t frame_delta = 0;
1862 if (tv && tv->is_track()) {
1863 speed = tv->track()->speed();
1866 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1872 switch (_operation) {
1874 trim_type = "Region start trim";
1877 trim_type = "Region end trim";
1880 trim_type = "Region content trim";
1884 _editor->begin_reversible_command (trim_type);
1886 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1887 RegionView* rv = i->view;
1888 rv->fake_set_opaque (false);
1889 rv->enable_display (false);
1890 rv->region()->playlist()->clear_owned_changes ();
1892 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1895 arv->temporarily_hide_envelope ();
1899 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1900 insert_result = _editor->motion_frozen_playlists.insert (pl);
1902 if (insert_result.second) {
1908 bool non_overlap_trim = false;
1910 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1911 non_overlap_trim = true;
1914 switch (_operation) {
1916 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1917 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1918 if (changed && _preserve_fade_anchor) {
1919 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1924 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1925 distance = _drags->current_pointer_x() - grab_x();
1926 len = ar->fade_in()->back()->when;
1927 new_length = len - _editor->pixel_to_sample (distance);
1928 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
1929 arv->reset_fade_in_shape_width (ar, new_length); //the grey shape
1936 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1937 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1938 if (changed && _preserve_fade_anchor) {
1939 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1944 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1945 distance = grab_x() - _drags->current_pointer_x();
1946 len = ar->fade_out()->back()->when;
1947 new_length = len - _editor->pixel_to_sample (distance);
1948 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
1949 arv->reset_fade_out_shape_width (ar, new_length); //the grey shape
1957 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1958 // frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1960 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1961 i->view->move_contents (frame_delta);
1967 switch (_operation) {
1969 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1972 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1975 // show_verbose_cursor_time (frame_delta);
1982 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1984 if (movement_occurred) {
1985 motion (event, false);
1987 if (_operation == StartTrim) {
1988 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1990 /* This must happen before the region's StatefulDiffCommand is created, as it may
1991 `correct' (ahem) the region's _start from being negative to being zero. It
1992 needs to be zero in the undo record.
1994 i->view->trim_front_ending ();
1996 if (_preserve_fade_anchor) {
1997 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2002 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2003 distance = _drags->current_pointer_x() - grab_x();
2004 len = ar->fade_in()->back()->when;
2005 new_length = len - _editor->pixel_to_sample (distance);
2006 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2007 ar->set_fade_in_length(new_length);
2011 } else if (_operation == EndTrim) {
2012 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2013 if (_preserve_fade_anchor) {
2014 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2019 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2020 distance = _drags->current_pointer_x() - grab_x();
2021 len = ar->fade_out()->back()->when;
2022 new_length = len - _editor->pixel_to_sample (distance);
2023 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2024 ar->set_fade_out_length(new_length);
2030 if (!_views.empty()) {
2031 if (_operation == StartTrim) {
2032 _editor->maybe_locate_with_edit_preroll(
2033 _views.begin()->view->region()->position());
2035 if (_operation == EndTrim) {
2036 _editor->maybe_locate_with_edit_preroll(
2037 _views.begin()->view->region()->position() +
2038 _views.begin()->view->region()->length());
2042 if (!_editor->selection->selected (_primary)) {
2043 _primary->thaw_after_trim ();
2046 set<boost::shared_ptr<Playlist> > diffed_playlists;
2048 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2049 i->view->thaw_after_trim ();
2050 i->view->enable_display (true);
2051 i->view->fake_set_opaque (true);
2053 /* Trimming one region may affect others on the playlist, so we need
2054 to get undo Commands from the whole playlist rather than just the
2055 region. Use diffed_playlists to make sure we don't diff a given
2056 playlist more than once.
2058 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2059 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2060 vector<Command*> cmds;
2062 _editor->session()->add_commands (cmds);
2063 diffed_playlists.insert (p);
2068 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2072 _editor->motion_frozen_playlists.clear ();
2073 _editor->commit_reversible_command();
2076 /* no mouse movement */
2077 _editor->point_trim (event, adjusted_current_frame (event));
2080 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2081 if (_operation == StartTrim) {
2082 i->view->trim_front_ending ();
2085 i->view->region()->resume_property_changes ();
2090 TrimDrag::aborted (bool movement_occurred)
2092 /* Our motion method is changing model state, so use the Undo system
2093 to cancel. Perhaps not ideal, as this will leave an Undo point
2094 behind which may be slightly odd from the user's point of view.
2099 if (movement_occurred) {
2103 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2104 i->view->region()->resume_property_changes ();
2109 TrimDrag::setup_pointer_frame_offset ()
2111 list<DraggingView>::iterator i = _views.begin ();
2112 while (i != _views.end() && i->view != _primary) {
2116 if (i == _views.end()) {
2120 switch (_operation) {
2122 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2125 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2132 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2136 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2137 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2142 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2144 Drag::start_grab (event, cursor);
2145 show_verbose_cursor_time (adjusted_current_frame(event));
2149 MeterMarkerDrag::setup_pointer_frame_offset ()
2151 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2155 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2157 if (!_marker->meter().movable()) {
2163 // create a dummy marker for visual representation of moving the
2164 // section, because whether its a copy or not, we're going to
2165 // leave or lose the original marker (leave if its a copy; lose if its
2166 // not, because we'll remove it from the map).
2168 MeterSection section (_marker->meter());
2170 if (!section.movable()) {
2175 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2177 _marker = new MeterMarker (
2179 *_editor->meter_group,
2180 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2182 *new MeterSection (_marker->meter())
2185 /* use the new marker for the grab */
2186 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2189 TempoMap& map (_editor->session()->tempo_map());
2190 /* get current state */
2191 before_state = &map.get_state();
2192 /* remove the section while we drag it */
2193 map.remove_meter (section, true);
2197 framepos_t const pf = adjusted_current_frame (event);
2198 _marker->set_position (pf);
2199 show_verbose_cursor_time (pf);
2203 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2205 if (!movement_occurred) {
2206 if (was_double_click()) {
2207 _editor->edit_meter_marker (*_marker);
2212 if (!_marker->meter().movable()) {
2216 motion (event, false);
2218 Timecode::BBT_Time when;
2220 TempoMap& map (_editor->session()->tempo_map());
2221 map.bbt_time (last_pointer_frame(), when);
2223 if (_copy == true) {
2224 _editor->begin_reversible_command (_("copy meter mark"));
2225 XMLNode &before = map.get_state();
2226 map.add_meter (_marker->meter(), when);
2227 XMLNode &after = map.get_state();
2228 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2229 _editor->commit_reversible_command ();
2232 _editor->begin_reversible_command (_("move meter mark"));
2234 /* we removed it before, so add it back now */
2236 map.add_meter (_marker->meter(), when);
2237 XMLNode &after = map.get_state();
2238 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2239 _editor->commit_reversible_command ();
2242 // delete the dummy marker we used for visual representation while moving.
2243 // a new visual marker will show up automatically.
2248 MeterMarkerDrag::aborted (bool moved)
2250 _marker->set_position (_marker->meter().frame ());
2253 TempoMap& map (_editor->session()->tempo_map());
2254 /* we removed it before, so add it back now */
2255 map.add_meter (_marker->meter(), _marker->meter().frame());
2256 // delete the dummy marker we used for visual representation while moving.
2257 // a new visual marker will show up automatically.
2262 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2266 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2268 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2273 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2275 Drag::start_grab (event, cursor);
2276 show_verbose_cursor_time (adjusted_current_frame (event));
2280 TempoMarkerDrag::setup_pointer_frame_offset ()
2282 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2286 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2288 if (!_marker->tempo().movable()) {
2294 // create a dummy marker for visual representation of moving the
2295 // section, because whether its a copy or not, we're going to
2296 // leave or lose the original marker (leave if its a copy; lose if its
2297 // not, because we'll remove it from the map).
2299 // create a dummy marker for visual representation of moving the copy.
2300 // The actual copying is not done before we reach the finish callback.
2303 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2305 TempoSection section (_marker->tempo());
2307 _marker = new TempoMarker (
2309 *_editor->tempo_group,
2310 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2312 *new TempoSection (_marker->tempo())
2315 /* use the new marker for the grab */
2316 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2319 TempoMap& map (_editor->session()->tempo_map());
2320 /* get current state */
2321 before_state = &map.get_state();
2322 /* remove the section while we drag it */
2323 map.remove_tempo (section, true);
2327 framepos_t const pf = adjusted_current_frame (event);
2328 _marker->set_position (pf);
2329 show_verbose_cursor_time (pf);
2333 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2335 if (!movement_occurred) {
2336 if (was_double_click()) {
2337 _editor->edit_tempo_marker (*_marker);
2342 if (!_marker->tempo().movable()) {
2346 motion (event, false);
2348 TempoMap& map (_editor->session()->tempo_map());
2349 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2350 Timecode::BBT_Time when;
2352 map.bbt_time (beat_time, when);
2354 if (_copy == true) {
2355 _editor->begin_reversible_command (_("copy tempo mark"));
2356 XMLNode &before = map.get_state();
2357 map.add_tempo (_marker->tempo(), when);
2358 XMLNode &after = map.get_state();
2359 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2360 _editor->commit_reversible_command ();
2363 _editor->begin_reversible_command (_("move tempo mark"));
2364 /* we removed it before, so add it back now */
2365 map.add_tempo (_marker->tempo(), when);
2366 XMLNode &after = map.get_state();
2367 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2368 _editor->commit_reversible_command ();
2371 // delete the dummy marker we used for visual representation while moving.
2372 // a new visual marker will show up automatically.
2377 TempoMarkerDrag::aborted (bool moved)
2379 _marker->set_position (_marker->tempo().frame());
2381 TempoMap& map (_editor->session()->tempo_map());
2382 /* we removed it before, so add it back now */
2383 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2384 // delete the dummy marker we used for visual representation while moving.
2385 // a new visual marker will show up automatically.
2390 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2391 : Drag (e, &c.time_bar_canvas_item())
2395 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2398 /** Do all the things we do when dragging the playhead to make it look as though
2399 * we have located, without actually doing the locate (because that would cause
2400 * the diskstream buffers to be refilled, which is too slow).
2403 CursorDrag::fake_locate (framepos_t t)
2405 _editor->playhead_cursor->set_position (t);
2407 Session* s = _editor->session ();
2408 if (s->timecode_transmission_suspended ()) {
2409 framepos_t const f = _editor->playhead_cursor->current_frame ();
2410 /* This is asynchronous so it will be sent "now"
2412 s->send_mmc_locate (f);
2413 /* These are synchronous and will be sent during the next
2416 s->queue_full_time_code ();
2417 s->queue_song_position_pointer ();
2420 show_verbose_cursor_time (t);
2421 _editor->UpdateAllTransportClocks (t);
2425 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2427 Drag::start_grab (event, c);
2429 _grab_zoom = _editor->samples_per_pixel;
2431 framepos_t where = _editor->canvas_event_frame (event);
2433 _editor->snap_to_with_modifier (where, event);
2435 _editor->_dragging_playhead = true;
2437 Session* s = _editor->session ();
2439 /* grab the track canvas item as well */
2441 _cursor.track_canvas_item().grab();
2444 if (_was_rolling && _stop) {
2448 if (s->is_auditioning()) {
2449 s->cancel_audition ();
2453 if (AudioEngine::instance()->connected()) {
2455 /* do this only if we're the engine is connected
2456 * because otherwise this request will never be
2457 * serviced and we'll busy wait forever. likewise,
2458 * notice if we are disconnected while waiting for the
2459 * request to be serviced.
2462 s->request_suspend_timecode_transmission ();
2463 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2464 /* twiddle our thumbs */
2469 fake_locate (where);
2473 CursorDrag::motion (GdkEvent* event, bool)
2475 framepos_t const adjusted_frame = adjusted_current_frame (event);
2476 if (adjusted_frame != last_pointer_frame()) {
2477 fake_locate (adjusted_frame);
2482 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2484 _editor->_dragging_playhead = false;
2486 _cursor.track_canvas_item().ungrab();
2488 if (!movement_occurred && _stop) {
2492 motion (event, false);
2494 Session* s = _editor->session ();
2496 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2497 _editor->_pending_locate_request = true;
2498 s->request_resume_timecode_transmission ();
2503 CursorDrag::aborted (bool)
2505 _cursor.track_canvas_item().ungrab();
2507 if (_editor->_dragging_playhead) {
2508 _editor->session()->request_resume_timecode_transmission ();
2509 _editor->_dragging_playhead = false;
2512 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2515 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2516 : RegionDrag (e, i, p, v)
2518 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2522 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2524 Drag::start_grab (event, cursor);
2526 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2527 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2529 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2533 FadeInDrag::setup_pointer_frame_offset ()
2535 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2536 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2537 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2541 FadeInDrag::motion (GdkEvent* event, bool)
2543 framecnt_t fade_length;
2545 framepos_t const pos = adjusted_current_frame (event);
2547 boost::shared_ptr<Region> region = _primary->region ();
2549 if (pos < (region->position() + 64)) {
2550 fade_length = 64; // this should be a minimum defined somewhere
2551 } else if (pos > region->last_frame()) {
2552 fade_length = region->length();
2554 fade_length = pos - region->position();
2557 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2559 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2565 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2568 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2572 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2574 if (!movement_occurred) {
2578 framecnt_t fade_length;
2580 framepos_t const pos = adjusted_current_frame (event);
2582 boost::shared_ptr<Region> region = _primary->region ();
2584 if (pos < (region->position() + 64)) {
2585 fade_length = 64; // this should be a minimum defined somewhere
2586 } else if (pos > region->last_frame()) {
2587 fade_length = region->length();
2589 fade_length = pos - region->position();
2592 _editor->begin_reversible_command (_("change fade in length"));
2594 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2596 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2602 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2603 XMLNode &before = alist->get_state();
2605 tmp->audio_region()->set_fade_in_length (fade_length);
2606 tmp->audio_region()->set_fade_in_active (true);
2608 XMLNode &after = alist->get_state();
2609 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2612 _editor->commit_reversible_command ();
2616 FadeInDrag::aborted (bool)
2618 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2619 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2625 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2629 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2630 : RegionDrag (e, i, p, v)
2632 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2636 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2638 Drag::start_grab (event, cursor);
2640 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2641 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2643 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2647 FadeOutDrag::setup_pointer_frame_offset ()
2649 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2650 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2651 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2655 FadeOutDrag::motion (GdkEvent* event, bool)
2657 framecnt_t fade_length;
2659 framepos_t const pos = adjusted_current_frame (event);
2661 boost::shared_ptr<Region> region = _primary->region ();
2663 if (pos > (region->last_frame() - 64)) {
2664 fade_length = 64; // this should really be a minimum fade defined somewhere
2666 else if (pos < region->position()) {
2667 fade_length = region->length();
2670 fade_length = region->last_frame() - pos;
2673 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2675 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2681 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2684 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2688 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2690 if (!movement_occurred) {
2694 framecnt_t fade_length;
2696 framepos_t const pos = adjusted_current_frame (event);
2698 boost::shared_ptr<Region> region = _primary->region ();
2700 if (pos > (region->last_frame() - 64)) {
2701 fade_length = 64; // this should really be a minimum fade defined somewhere
2703 else if (pos < region->position()) {
2704 fade_length = region->length();
2707 fade_length = region->last_frame() - pos;
2710 _editor->begin_reversible_command (_("change fade out length"));
2712 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2714 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2720 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2721 XMLNode &before = alist->get_state();
2723 tmp->audio_region()->set_fade_out_length (fade_length);
2724 tmp->audio_region()->set_fade_out_active (true);
2726 XMLNode &after = alist->get_state();
2727 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2730 _editor->commit_reversible_command ();
2734 FadeOutDrag::aborted (bool)
2736 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2737 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2743 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2747 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2750 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2752 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2755 _points.push_back (ArdourCanvas::Duple (0, 0));
2756 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2759 MarkerDrag::~MarkerDrag ()
2761 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2766 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2768 location = new Location (*l);
2769 markers.push_back (m);
2774 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2776 Drag::start_grab (event, cursor);
2780 Location *location = _editor->find_location_from_marker (_marker, is_start);
2781 _editor->_dragging_edit_point = true;
2783 update_item (location);
2785 // _drag_line->show();
2786 // _line->raise_to_top();
2789 show_verbose_cursor_time (location->start());
2791 show_verbose_cursor_time (location->end());
2794 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2797 case Selection::Toggle:
2798 /* we toggle on the button release */
2800 case Selection::Set:
2801 if (!_editor->selection->selected (_marker)) {
2802 _editor->selection->set (_marker);
2805 case Selection::Extend:
2807 Locations::LocationList ll;
2808 list<Marker*> to_add;
2810 _editor->selection->markers.range (s, e);
2811 s = min (_marker->position(), s);
2812 e = max (_marker->position(), e);
2815 if (e < max_framepos) {
2818 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2819 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2820 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2823 to_add.push_back (lm->start);
2826 to_add.push_back (lm->end);
2830 if (!to_add.empty()) {
2831 _editor->selection->add (to_add);
2835 case Selection::Add:
2836 _editor->selection->add (_marker);
2840 /* Set up copies for us to manipulate during the drag
2843 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2845 Location* l = _editor->find_location_from_marker (*i, is_start);
2852 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2854 /* range: check that the other end of the range isn't
2857 CopiedLocationInfo::iterator x;
2858 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2859 if (*(*x).location == *l) {
2863 if (x == _copied_locations.end()) {
2864 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2866 (*x).markers.push_back (*i);
2867 (*x).move_both = true;
2875 MarkerDrag::setup_pointer_frame_offset ()
2878 Location *location = _editor->find_location_from_marker (_marker, is_start);
2879 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2883 MarkerDrag::motion (GdkEvent* event, bool)
2885 framecnt_t f_delta = 0;
2887 bool move_both = false;
2888 Location *real_location;
2889 Location *copy_location = 0;
2891 framepos_t const newframe = adjusted_current_frame (event);
2892 framepos_t next = newframe;
2894 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2898 CopiedLocationInfo::iterator x;
2900 /* find the marker we're dragging, and compute the delta */
2902 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2904 copy_location = (*x).location;
2906 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2908 /* this marker is represented by this
2909 * CopiedLocationMarkerInfo
2912 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
2917 if (real_location->is_mark()) {
2918 f_delta = newframe - copy_location->start();
2922 switch (_marker->type()) {
2923 case Marker::SessionStart:
2924 case Marker::RangeStart:
2925 case Marker::LoopStart:
2926 case Marker::PunchIn:
2927 f_delta = newframe - copy_location->start();
2930 case Marker::SessionEnd:
2931 case Marker::RangeEnd:
2932 case Marker::LoopEnd:
2933 case Marker::PunchOut:
2934 f_delta = newframe - copy_location->end();
2937 /* what kind of marker is this ? */
2946 if (x == _copied_locations.end()) {
2947 /* hmm, impossible - we didn't find the dragged marker */
2951 /* now move them all */
2953 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2955 copy_location = x->location;
2957 /* call this to find out if its the start or end */
2959 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
2963 if (real_location->locked()) {
2967 if (copy_location->is_mark()) {
2971 copy_location->set_start (copy_location->start() + f_delta);
2975 framepos_t new_start = copy_location->start() + f_delta;
2976 framepos_t new_end = copy_location->end() + f_delta;
2978 if (is_start) { // start-of-range marker
2980 if (move_both || (*x).move_both) {
2981 copy_location->set_start (new_start);
2982 copy_location->set_end (new_end);
2983 } else if (new_start < copy_location->end()) {
2984 copy_location->set_start (new_start);
2985 } else if (newframe > 0) {
2986 _editor->snap_to (next, 1, true);
2987 copy_location->set_end (next);
2988 copy_location->set_start (newframe);
2991 } else { // end marker
2993 if (move_both || (*x).move_both) {
2994 copy_location->set_end (new_end);
2995 copy_location->set_start (new_start);
2996 } else if (new_end > copy_location->start()) {
2997 copy_location->set_end (new_end);
2998 } else if (newframe > 0) {
2999 _editor->snap_to (next, -1, true);
3000 copy_location->set_start (next);
3001 copy_location->set_end (newframe);
3006 update_item (copy_location);
3008 /* now lookup the actual GUI items used to display this
3009 * location and move them to wherever the copy of the location
3010 * is now. This means that the logic in ARDOUR::Location is
3011 * still enforced, even though we are not (yet) modifying
3012 * the real Location itself.
3015 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3018 lm->set_position (copy_location->start(), copy_location->end());
3023 assert (!_copied_locations.empty());
3025 show_verbose_cursor_time (newframe);
3029 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3031 if (!movement_occurred) {
3033 if (was_double_click()) {
3034 _editor->rename_marker (_marker);
3038 /* just a click, do nothing but finish
3039 off the selection process
3042 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3045 case Selection::Set:
3046 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3047 _editor->selection->set (_marker);
3051 case Selection::Toggle:
3052 /* we toggle on the button release, click only */
3053 _editor->selection->toggle (_marker);
3056 case Selection::Extend:
3057 case Selection::Add:
3064 _editor->_dragging_edit_point = false;
3066 _editor->begin_reversible_command ( _("move marker") );
3067 XMLNode &before = _editor->session()->locations()->get_state();
3069 MarkerSelection::iterator i;
3070 CopiedLocationInfo::iterator x;
3073 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3074 x != _copied_locations.end() && i != _editor->selection->markers.end();
3077 Location * location = _editor->find_location_from_marker (*i, is_start);
3081 if (location->locked()) {
3085 if (location->is_mark()) {
3086 location->set_start (((*x).location)->start());
3088 location->set (((*x).location)->start(), ((*x).location)->end());
3093 XMLNode &after = _editor->session()->locations()->get_state();
3094 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3095 _editor->commit_reversible_command ();
3099 MarkerDrag::aborted (bool)
3105 MarkerDrag::update_item (Location*)
3110 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3112 _cumulative_x_drag (0),
3113 _cumulative_y_drag (0)
3115 if (_zero_gain_fraction < 0.0) {
3116 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3119 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3121 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3127 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3129 Drag::start_grab (event, _editor->cursors()->fader);
3131 // start the grab at the center of the control point so
3132 // the point doesn't 'jump' to the mouse after the first drag
3133 _fixed_grab_x = _point->get_x();
3134 _fixed_grab_y = _point->get_y();
3136 float const fraction = 1 - (_point->get_y() / _point->line().height());
3138 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3140 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3141 event->button.x + 10, event->button.y + 10);
3143 _editor->verbose_cursor()->show ();
3145 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3147 if (!_point->can_slide ()) {
3148 _x_constrained = true;
3153 ControlPointDrag::motion (GdkEvent* event, bool)
3155 double dx = _drags->current_pointer_x() - last_pointer_x();
3156 double dy = _drags->current_pointer_y() - last_pointer_y();
3158 if (event->button.state & Keyboard::SecondaryModifier) {
3163 /* coordinate in pixels relative to the start of the region (for region-based automation)
3164 or track (for track-based automation) */
3165 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3166 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3168 // calculate zero crossing point. back off by .01 to stay on the
3169 // positive side of zero
3170 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3172 // make sure we hit zero when passing through
3173 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3177 if (_x_constrained) {
3180 if (_y_constrained) {
3184 _cumulative_x_drag = cx - _fixed_grab_x;
3185 _cumulative_y_drag = cy - _fixed_grab_y;
3189 cy = min ((double) _point->line().height(), cy);
3191 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3193 if (!_x_constrained) {
3194 _editor->snap_to_with_modifier (cx_frames, event);
3197 cx_frames = min (cx_frames, _point->line().maximum_time());
3199 float const fraction = 1.0 - (cy / _point->line().height());
3201 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3203 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3207 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3209 if (!movement_occurred) {
3213 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3214 _editor->reset_point_selection ();
3218 motion (event, false);
3221 _point->line().end_drag (_pushing, _final_index);
3222 _editor->session()->commit_reversible_command ();
3226 ControlPointDrag::aborted (bool)
3228 _point->line().reset ();
3232 ControlPointDrag::active (Editing::MouseMode m)
3234 if (m == Editing::MouseGain) {
3235 /* always active in mouse gain */
3239 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3240 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3243 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3246 _cumulative_y_drag (0)
3248 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3252 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3254 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3257 _item = &_line->grab_item ();
3259 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3260 origin, and ditto for y.
3263 double cx = event->button.x;
3264 double cy = event->button.y;
3266 _line->parent_group().canvas_to_item (cx, cy);
3268 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3273 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3274 /* no adjacent points */
3278 Drag::start_grab (event, _editor->cursors()->fader);
3280 /* store grab start in parent frame */
3285 double fraction = 1.0 - (cy / _line->height());
3287 _line->start_drag_line (before, after, fraction);
3289 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3290 event->button.x + 10, event->button.y + 10);
3292 _editor->verbose_cursor()->show ();
3296 LineDrag::motion (GdkEvent* event, bool)
3298 double dy = _drags->current_pointer_y() - last_pointer_y();
3300 if (event->button.state & Keyboard::SecondaryModifier) {
3304 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3306 _cumulative_y_drag = cy - _fixed_grab_y;
3309 cy = min ((double) _line->height(), cy);
3311 double const fraction = 1.0 - (cy / _line->height());
3314 /* we are ignoring x position for this drag, so we can just pass in anything */
3315 _line->drag_motion (0, fraction, true, false, ignored);
3317 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3321 LineDrag::finished (GdkEvent* event, bool movement_occured)
3323 if (movement_occured) {
3324 motion (event, false);
3325 _line->end_drag (false, 0);
3327 /* add a new control point on the line */
3329 AutomationTimeAxisView* atv;
3331 _line->end_drag (false, 0);
3333 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3334 framepos_t where = _editor->window_event_frame (event, 0, 0);
3335 atv->add_automation_event (event, where, event->button.y, false);
3339 _editor->session()->commit_reversible_command ();
3343 LineDrag::aborted (bool)
3348 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3351 _cumulative_x_drag (0)
3353 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3357 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3359 Drag::start_grab (event);
3361 _line = reinterpret_cast<Line*> (_item);
3364 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3366 double cx = event->button.x;
3367 double cy = event->button.y;
3369 _item->parent()->canvas_to_item (cx, cy);
3371 /* store grab start in parent frame */
3372 _region_view_grab_x = cx;
3374 _before = *(float*) _item->get_data ("position");
3376 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3378 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3382 FeatureLineDrag::motion (GdkEvent*, bool)
3384 double dx = _drags->current_pointer_x() - last_pointer_x();
3386 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3388 _cumulative_x_drag += dx;
3390 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3399 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3401 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3403 float *pos = new float;
3406 _line->set_data ("position", pos);
3412 FeatureLineDrag::finished (GdkEvent*, bool)
3414 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3415 _arv->update_transient(_before, _before);
3419 FeatureLineDrag::aborted (bool)
3424 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3426 , _vertical_only (false)
3428 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3432 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3434 Drag::start_grab (event);
3435 show_verbose_cursor_time (adjusted_current_frame (event));
3439 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3446 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3448 framepos_t grab = grab_frame ();
3449 if (Config->get_rubberbanding_snaps_to_grid ()) {
3450 _editor->snap_to_with_modifier (grab, event);
3453 /* base start and end on initial click position */
3463 if (_drags->current_pointer_y() < grab_y()) {
3464 y1 = _drags->current_pointer_y();
3467 y2 = _drags->current_pointer_y();
3472 if (start != end || y1 != y2) {
3474 double x1 = _editor->sample_to_pixel (start);
3475 double x2 = _editor->sample_to_pixel (end);
3476 const double min_dimension = 2.0;
3478 _editor->rubberband_rect->set_x0 (x1);
3479 if (_vertical_only) {
3480 /* fixed 10 pixel width */
3481 _editor->rubberband_rect->set_x1 (x1 + 10);
3484 x2 = min (x1 - min_dimension, x2);
3486 x2 = max (x1 + min_dimension, x2);
3488 _editor->rubberband_rect->set_x1 (x2);
3491 _editor->rubberband_rect->set_y0 (y1);
3493 y2 = min (y1 - min_dimension, y2);
3495 y2 = max (y1 + min_dimension, y2);
3498 _editor->rubberband_rect->set_y1 (y2);
3500 _editor->rubberband_rect->show();
3501 _editor->rubberband_rect->raise_to_top();
3503 show_verbose_cursor_time (pf);
3505 do_select_things (event, true);
3510 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3515 if (grab_frame() < last_pointer_frame()) {
3517 x2 = last_pointer_frame ();
3520 x1 = last_pointer_frame ();
3526 if (_drags->current_pointer_y() < grab_y()) {
3527 y1 = _drags->current_pointer_y();
3530 y2 = _drags->current_pointer_y();
3534 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3538 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3540 if (movement_occurred) {
3542 motion (event, false);
3543 do_select_things (event, false);
3549 bool do_deselect = true;
3550 MidiTimeAxisView* mtv;
3552 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3554 if (_editor->selection->empty()) {
3555 /* nothing selected */
3556 add_midi_region (mtv);
3557 do_deselect = false;
3561 /* do not deselect if Primary or Tertiary (toggle-select or
3562 * extend-select are pressed.
3565 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3566 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3573 _editor->rubberband_rect->hide();
3577 RubberbandSelectDrag::aborted (bool)
3579 _editor->rubberband_rect->hide ();
3582 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3583 : RegionDrag (e, i, p, v)
3585 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3589 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3591 Drag::start_grab (event, cursor);
3593 show_verbose_cursor_time (adjusted_current_frame (event));
3597 TimeFXDrag::motion (GdkEvent* event, bool)
3599 RegionView* rv = _primary;
3600 StreamView* cv = rv->get_time_axis_view().view ();
3602 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3603 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3604 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3606 framepos_t const pf = adjusted_current_frame (event);
3608 if (pf > rv->region()->position()) {
3609 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3612 show_verbose_cursor_time (pf);
3616 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3618 _primary->get_time_axis_view().hide_timestretch ();
3620 if (!movement_occurred) {
3624 if (last_pointer_frame() < _primary->region()->position()) {
3625 /* backwards drag of the left edge - not usable */
3629 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3631 float percentage = (double) newlen / (double) _primary->region()->length();
3633 #ifndef USE_RUBBERBAND
3634 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3635 if (_primary->region()->data_type() == DataType::AUDIO) {
3636 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3640 if (!_editor->get_selection().regions.empty()) {
3641 /* primary will already be included in the selection, and edit
3642 group shared editing will propagate selection across
3643 equivalent regions, so just use the current region
3647 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3648 error << _("An error occurred while executing time stretch operation") << endmsg;
3654 TimeFXDrag::aborted (bool)
3656 _primary->get_time_axis_view().hide_timestretch ();
3659 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3662 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3666 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3668 Drag::start_grab (event);
3672 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3674 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3678 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3680 if (movement_occurred && _editor->session()) {
3681 /* make sure we stop */
3682 _editor->session()->request_transport_speed (0.0);
3687 ScrubDrag::aborted (bool)
3692 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3697 , _original_pointer_time_axis (-1)
3698 , _last_pointer_time_axis (-1)
3699 , _time_selection_at_start (!_editor->get_selection().time.empty())
3701 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3703 if (_time_selection_at_start) {
3704 start_at_start = _editor->get_selection().time.start();
3705 end_at_start = _editor->get_selection().time.end_frame();
3710 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3712 if (_editor->session() == 0) {
3716 Gdk::Cursor* cursor = 0;
3718 switch (_operation) {
3719 case CreateSelection:
3720 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3725 cursor = _editor->cursors()->selector;
3726 Drag::start_grab (event, cursor);
3729 case SelectionStartTrim:
3730 if (_editor->clicked_axisview) {
3731 _editor->clicked_axisview->order_selection_trims (_item, true);
3733 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3736 case SelectionEndTrim:
3737 if (_editor->clicked_axisview) {
3738 _editor->clicked_axisview->order_selection_trims (_item, false);
3740 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3744 Drag::start_grab (event, cursor);
3747 case SelectionExtend:
3748 Drag::start_grab (event, cursor);
3752 if (_operation == SelectionMove) {
3753 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3755 show_verbose_cursor_time (adjusted_current_frame (event));
3758 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3762 SelectionDrag::setup_pointer_frame_offset ()
3764 switch (_operation) {
3765 case CreateSelection:
3766 _pointer_frame_offset = 0;
3769 case SelectionStartTrim:
3771 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3774 case SelectionEndTrim:
3775 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3778 case SelectionExtend:
3784 SelectionDrag::motion (GdkEvent* event, bool first_move)
3786 framepos_t start = 0;
3788 framecnt_t length = 0;
3789 framecnt_t distance = 0;
3791 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3792 if (pending_time_axis.first == 0) {
3796 framepos_t const pending_position = adjusted_current_frame (event);
3798 /* only alter selection if things have changed */
3800 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3804 switch (_operation) {
3805 case CreateSelection:
3807 framepos_t grab = grab_frame ();
3810 grab = adjusted_current_frame (event, false);
3811 if (grab < pending_position) {
3812 _editor->snap_to (grab, -1);
3814 _editor->snap_to (grab, 1);
3818 if (pending_position < grab) {
3819 start = pending_position;
3822 end = pending_position;
3826 /* first drag: Either add to the selection
3827 or create a new selection
3833 /* adding to the selection */
3834 _editor->set_selected_track_as_side_effect (Selection::Add);
3835 //_editor->selection->add (_editor->clicked_axisview);
3836 _editor->clicked_selection = _editor->selection->add (start, end);
3841 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3842 //_editor->selection->set (_editor->clicked_axisview);
3843 _editor->set_selected_track_as_side_effect (Selection::Set);
3846 _editor->clicked_selection = _editor->selection->set (start, end);
3850 /* select the track that we're in */
3851 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3852 // _editor->set_selected_track_as_side_effect (Selection::Add);
3853 _editor->selection->add (pending_time_axis.first);
3854 _added_time_axes.push_back (pending_time_axis.first);
3857 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3858 tracks that we selected in the first place.
3861 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3862 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3864 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3865 while (i != _added_time_axes.end()) {
3867 list<TimeAxisView*>::iterator tmp = i;
3870 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3871 _editor->selection->remove (*i);
3872 _added_time_axes.remove (*i);
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 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
3925 _editor->start_canvas_autoscroll (1, 0);
3929 switch (_operation) {
3931 if (_time_selection_at_start) {
3932 _editor->selection->move_time (distance);
3936 _editor->selection->replace (_editor->clicked_selection, start, end);
3940 if (_operation == SelectionMove) {
3941 show_verbose_cursor_time(start);
3943 show_verbose_cursor_time(pending_position);
3948 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3950 Session* s = _editor->session();
3952 if (movement_occurred) {
3953 motion (event, false);
3954 /* XXX this is not object-oriented programming at all. ick */
3955 if (_editor->selection->time.consolidate()) {
3956 _editor->selection->TimeChanged ();
3959 /* XXX what if its a music time selection? */
3961 if ( s->get_play_range() && s->transport_rolling() ) {
3962 s->request_play_range (&_editor->selection->time, true);
3964 if (Config->get_always_play_range() && !s->transport_rolling()) {
3965 s->request_locate (_editor->get_selection().time.start());
3971 /* just a click, no pointer movement.
3974 if (_operation == SelectionExtend) {
3975 if (_time_selection_at_start) {
3976 framepos_t pos = adjusted_current_frame (event, false);
3977 framepos_t start = min (pos, start_at_start);
3978 framepos_t end = max (pos, end_at_start);
3979 _editor->selection->set (start, end);
3982 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3983 if (_editor->clicked_selection) {
3984 _editor->selection->remove (_editor->clicked_selection);
3987 if (!_editor->clicked_selection) {
3988 _editor->selection->clear_time();
3993 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3994 _editor->selection->set (_editor->clicked_axisview);
3997 if (s && s->get_play_range () && s->transport_rolling()) {
3998 s->request_stop (false, false);
4003 _editor->stop_canvas_autoscroll ();
4004 _editor->clicked_selection = 0;
4008 SelectionDrag::aborted (bool)
4013 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4018 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4020 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4021 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4022 physical_screen_height (_editor->get_window())));
4023 _drag_rect->hide ();
4025 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4026 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4030 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4032 if (_editor->session() == 0) {
4036 Gdk::Cursor* cursor = 0;
4038 if (!_editor->temp_location) {
4039 _editor->temp_location = new Location (*_editor->session());
4042 switch (_operation) {
4043 case CreateRangeMarker:
4044 case CreateTransportMarker:
4045 case CreateCDMarker:
4047 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4052 cursor = _editor->cursors()->selector;
4056 Drag::start_grab (event, cursor);
4058 show_verbose_cursor_time (adjusted_current_frame (event));
4062 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4064 framepos_t start = 0;
4066 ArdourCanvas::Rectangle *crect;
4068 switch (_operation) {
4069 case CreateRangeMarker:
4070 crect = _editor->range_bar_drag_rect;
4072 case CreateTransportMarker:
4073 crect = _editor->transport_bar_drag_rect;
4075 case CreateCDMarker:
4076 crect = _editor->cd_marker_bar_drag_rect;
4079 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4084 framepos_t const pf = adjusted_current_frame (event);
4086 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4087 framepos_t grab = grab_frame ();
4088 _editor->snap_to (grab);
4090 if (pf < grab_frame()) {
4098 /* first drag: Either add to the selection
4099 or create a new selection.
4104 _editor->temp_location->set (start, end);
4108 update_item (_editor->temp_location);
4110 //_drag_rect->raise_to_top();
4115 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
4116 _editor->start_canvas_autoscroll (1, 0);
4120 _editor->temp_location->set (start, end);
4122 double x1 = _editor->sample_to_pixel (start);
4123 double x2 = _editor->sample_to_pixel (end);
4127 update_item (_editor->temp_location);
4130 show_verbose_cursor_time (pf);
4135 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4137 Location * newloc = 0;
4141 if (movement_occurred) {
4142 motion (event, false);
4145 switch (_operation) {
4146 case CreateRangeMarker:
4147 case CreateCDMarker:
4149 _editor->begin_reversible_command (_("new range marker"));
4150 XMLNode &before = _editor->session()->locations()->get_state();
4151 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4152 if (_operation == CreateCDMarker) {
4153 flags = Location::IsRangeMarker | Location::IsCDMarker;
4154 _editor->cd_marker_bar_drag_rect->hide();
4157 flags = Location::IsRangeMarker;
4158 _editor->range_bar_drag_rect->hide();
4160 newloc = new Location (
4161 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4164 _editor->session()->locations()->add (newloc, true);
4165 XMLNode &after = _editor->session()->locations()->get_state();
4166 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4167 _editor->commit_reversible_command ();
4171 case CreateTransportMarker:
4172 // popup menu to pick loop or punch
4173 _editor->new_transport_marker_context_menu (&event->button, _item);
4179 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4181 if (_operation == CreateTransportMarker) {
4183 /* didn't drag, so just locate */
4185 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4187 } else if (_operation == CreateCDMarker) {
4189 /* didn't drag, but mark is already created so do
4192 } else { /* operation == CreateRangeMarker */
4198 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4200 if (end == max_framepos) {
4201 end = _editor->session()->current_end_frame ();
4204 if (start == max_framepos) {
4205 start = _editor->session()->current_start_frame ();
4208 switch (_editor->mouse_mode) {
4210 /* find the two markers on either side and then make the selection from it */
4211 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4215 /* find the two markers on either side of the click and make the range out of it */
4216 _editor->selection->set (start, end);
4225 _editor->stop_canvas_autoscroll ();
4229 RangeMarkerBarDrag::aborted (bool)
4235 RangeMarkerBarDrag::update_item (Location* location)
4237 double const x1 = _editor->sample_to_pixel (location->start());
4238 double const x2 = _editor->sample_to_pixel (location->end());
4240 _drag_rect->set_x0 (x1);
4241 _drag_rect->set_x1 (x2);
4244 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4248 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4252 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4254 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4255 Drag::start_grab (event, _editor->cursors()->zoom_out);
4258 Drag::start_grab (event, _editor->cursors()->zoom_in);
4262 show_verbose_cursor_time (adjusted_current_frame (event));
4266 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4271 framepos_t const pf = adjusted_current_frame (event);
4273 framepos_t grab = grab_frame ();
4274 _editor->snap_to_with_modifier (grab, event);
4276 /* base start and end on initial click position */
4288 _editor->zoom_rect->show();
4289 _editor->zoom_rect->raise_to_top();
4292 _editor->reposition_zoom_rect(start, end);
4294 show_verbose_cursor_time (pf);
4299 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4301 if (movement_occurred) {
4302 motion (event, false);
4304 if (grab_frame() < last_pointer_frame()) {
4305 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4307 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4310 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4311 _editor->tav_zoom_step (_zoom_out);
4313 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4317 _editor->zoom_rect->hide();
4321 MouseZoomDrag::aborted (bool)
4323 _editor->zoom_rect->hide ();
4326 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4328 , _cumulative_dx (0)
4329 , _cumulative_dy (0)
4331 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4333 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4335 _region = &_primary->region_view ();
4336 _note_height = _region->midi_stream_view()->note_height ();
4340 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4342 Drag::start_grab (event);
4344 if (!(_was_selected = _primary->selected())) {
4346 /* tertiary-click means extend selection - we'll do that on button release,
4347 so don't add it here, because otherwise we make it hard to figure
4348 out the "extend-to" range.
4351 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4354 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4357 _region->note_selected (_primary, true);
4359 _region->unique_select (_primary);
4365 /** @return Current total drag x change in frames */
4367 NoteDrag::total_dx () const
4370 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4372 /* primary note time */
4373 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4375 /* new time of the primary note in session frames */
4376 frameoffset_t st = n + dx;
4378 framepos_t const rp = _region->region()->position ();
4380 /* prevent the note being dragged earlier than the region's position */
4383 /* snap and return corresponding delta */
4384 return _region->snap_frame_to_frame (st - rp) + rp - n;
4387 /** @return Current total drag y change in note number */
4389 NoteDrag::total_dy () const
4391 MidiStreamView* msv = _region->midi_stream_view ();
4392 double const y = _region->midi_view()->y_position ();
4393 /* new current note */
4394 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4396 n = max (msv->lowest_note(), n);
4397 n = min (msv->highest_note(), n);
4398 /* and work out delta */
4399 return n - msv->y_to_note (grab_y() - y);
4403 NoteDrag::motion (GdkEvent *, bool)
4405 /* Total change in x and y since the start of the drag */
4406 frameoffset_t const dx = total_dx ();
4407 int8_t const dy = total_dy ();
4409 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4410 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4411 double const tdy = -dy * _note_height - _cumulative_dy;
4414 _cumulative_dx += tdx;
4415 _cumulative_dy += tdy;
4417 int8_t note_delta = total_dy();
4419 _region->move_selection (tdx, tdy, note_delta);
4421 /* the new note value may be the same as the old one, but we
4422 * don't know what that means because the selection may have
4423 * involved more than one note and we might be doing something
4424 * odd with them. so show the note value anyway, always.
4428 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4430 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4431 (int) floor ((double)new_note));
4433 show_verbose_cursor_text (buf);
4438 NoteDrag::finished (GdkEvent* ev, bool moved)
4441 /* no motion - select note */
4443 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4444 _editor->current_mouse_mode() == Editing::MouseDraw) {
4446 if (_was_selected) {
4447 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4449 _region->note_deselected (_primary);
4452 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4453 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4455 if (!extend && !add && _region->selection_size() > 1) {
4456 _region->unique_select (_primary);
4457 } else if (extend) {
4458 _region->note_selected (_primary, true, true);
4460 /* it was added during button press */
4465 _region->note_dropped (_primary, total_dx(), total_dy());
4470 NoteDrag::aborted (bool)
4475 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4476 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4477 : Drag (editor, atv->base_item ())
4479 , _nothing_to_drag (false)
4481 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4482 y_origin = atv->y_position();
4483 setup (atv->lines ());
4486 /** Make an AutomationRangeDrag for region gain lines */
4487 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4488 : Drag (editor, rv->get_canvas_group ())
4490 , _nothing_to_drag (false)
4492 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4494 list<boost::shared_ptr<AutomationLine> > lines;
4495 lines.push_back (rv->get_gain_line ());
4496 y_origin = rv->get_time_axis_view().y_position();
4500 /** @param lines AutomationLines to drag.
4501 * @param offset Offset from the session start to the points in the AutomationLines.
4504 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4506 /* find the lines that overlap the ranges being dragged */
4507 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4508 while (i != lines.end ()) {
4509 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4512 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4514 /* check this range against all the AudioRanges that we are using */
4515 list<AudioRange>::const_iterator k = _ranges.begin ();
4516 while (k != _ranges.end()) {
4517 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4523 /* add it to our list if it overlaps at all */
4524 if (k != _ranges.end()) {
4529 _lines.push_back (n);
4535 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4539 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4541 return 1.0 - ((global_y - y_origin) / line->height());
4545 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4547 Drag::start_grab (event, cursor);
4549 /* Get line states before we start changing things */
4550 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4551 i->state = &i->line->get_state ();
4552 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4555 if (_ranges.empty()) {
4557 /* No selected time ranges: drag all points */
4558 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4559 uint32_t const N = i->line->npoints ();
4560 for (uint32_t j = 0; j < N; ++j) {
4561 i->points.push_back (i->line->nth (j));
4567 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4569 framecnt_t const half = (i->start + i->end) / 2;
4571 /* find the line that this audio range starts in */
4572 list<Line>::iterator j = _lines.begin();
4573 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4577 if (j != _lines.end()) {
4578 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4580 /* j is the line that this audio range starts in; fade into it;
4581 64 samples length plucked out of thin air.
4584 framepos_t a = i->start + 64;
4589 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4590 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4592 the_list->add (p, the_list->eval (p));
4593 the_list->add (q, the_list->eval (q));
4596 /* same thing for the end */
4599 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4603 if (j != _lines.end()) {
4604 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4606 /* j is the line that this audio range starts in; fade out of it;
4607 64 samples length plucked out of thin air.
4610 framepos_t b = i->end - 64;
4615 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4616 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4618 the_list->add (p, the_list->eval (p));
4619 the_list->add (q, the_list->eval (q));
4623 _nothing_to_drag = true;
4625 /* Find all the points that should be dragged and put them in the relevant
4626 points lists in the Line structs.
4629 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4631 uint32_t const N = i->line->npoints ();
4632 for (uint32_t j = 0; j < N; ++j) {
4634 /* here's a control point on this line */
4635 ControlPoint* p = i->line->nth (j);
4636 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4638 /* see if it's inside a range */
4639 list<AudioRange>::const_iterator k = _ranges.begin ();
4640 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4644 if (k != _ranges.end()) {
4645 /* dragging this point */
4646 _nothing_to_drag = false;
4647 i->points.push_back (p);
4653 if (_nothing_to_drag) {
4657 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4658 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4663 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4665 if (_nothing_to_drag) {
4669 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4670 float const f = y_fraction (l->line, _drags->current_pointer_y());
4671 /* we are ignoring x position for this drag, so we can just pass in anything */
4673 l->line->drag_motion (0, f, true, false, ignored);
4674 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4679 AutomationRangeDrag::finished (GdkEvent* event, bool)
4681 if (_nothing_to_drag) {
4685 motion (event, false);
4686 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4687 i->line->end_drag (false, 0);
4690 _editor->session()->commit_reversible_command ();
4694 AutomationRangeDrag::aborted (bool)
4696 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4701 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4704 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4705 layer = v->region()->layer ();
4706 initial_y = v->get_canvas_group()->position().y;
4707 initial_playlist = v->region()->playlist ();
4708 initial_position = v->region()->position ();
4709 initial_end = v->region()->position () + v->region()->length ();
4712 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4713 : Drag (e, i->canvas_item ())
4716 , _cumulative_dx (0)
4718 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4719 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4724 PatchChangeDrag::motion (GdkEvent* ev, bool)
4726 framepos_t f = adjusted_current_frame (ev);
4727 boost::shared_ptr<Region> r = _region_view->region ();
4728 f = max (f, r->position ());
4729 f = min (f, r->last_frame ());
4731 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4732 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4733 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4734 _cumulative_dx = dxu;
4738 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4740 if (!movement_occurred) {
4744 boost::shared_ptr<Region> r (_region_view->region ());
4745 framepos_t f = adjusted_current_frame (ev);
4746 f = max (f, r->position ());
4747 f = min (f, r->last_frame ());
4749 _region_view->move_patch_change (
4751 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4756 PatchChangeDrag::aborted (bool)
4758 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4762 PatchChangeDrag::setup_pointer_frame_offset ()
4764 boost::shared_ptr<Region> region = _region_view->region ();
4765 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4768 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4769 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4776 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4778 framepos_t const p = _region_view->region()->position ();
4779 double const y = _region_view->midi_view()->y_position ();
4781 x1 = max ((framepos_t) 0, x1 - p);
4782 x2 = max ((framepos_t) 0, x2 - p);
4783 y1 = max (0.0, y1 - y);
4784 y2 = max (0.0, y2 - y);
4786 _region_view->update_drag_selection (
4787 _editor->sample_to_pixel (x1),
4788 _editor->sample_to_pixel (x2),
4791 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4796 MidiRubberbandSelectDrag::deselect_things ()
4801 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4802 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4805 _vertical_only = true;
4809 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4811 double const y = _region_view->midi_view()->y_position ();
4813 y1 = max (0.0, y1 - y);
4814 y2 = max (0.0, y2 - y);
4816 _region_view->update_vertical_drag_selection (
4819 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4824 MidiVerticalSelectDrag::deselect_things ()
4829 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4830 : RubberbandSelectDrag (e, i)
4836 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4838 if (drag_in_progress) {
4839 /* We just want to select things at the end of the drag, not during it */
4843 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4845 _editor->begin_reversible_command (_("rubberband selection"));
4846 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4847 _editor->commit_reversible_command ();
4851 EditorRubberbandSelectDrag::deselect_things ()
4853 if (!getenv("ARDOUR_SAE")) {
4854 _editor->selection->clear_tracks();
4856 _editor->selection->clear_regions();
4857 _editor->selection->clear_points ();
4858 _editor->selection->clear_lines ();
4861 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4869 NoteCreateDrag::~NoteCreateDrag ()
4875 NoteCreateDrag::grid_frames (framepos_t t) const
4878 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4883 return _region_view->region_beats_to_region_frames (grid_beats);
4887 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4889 Drag::start_grab (event, cursor);
4891 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4893 framepos_t pf = _drags->current_pointer_frame ();
4894 framecnt_t const g = grid_frames (pf);
4896 /* Hack so that we always snap to the note that we are over, instead of snapping
4897 to the next one if we're more than halfway through the one we're over.
4899 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4903 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4905 MidiStreamView* sv = _region_view->midi_stream_view ();
4906 double const x = _editor->sample_to_pixel (_note[0]);
4907 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4909 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4910 _drag_rect->set_outline_what (0xff);
4911 _drag_rect->set_outline_color (0xffffff99);
4912 _drag_rect->set_fill_color (0xffffff66);
4916 NoteCreateDrag::motion (GdkEvent* event, bool)
4918 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4919 double const x = _editor->sample_to_pixel (_note[1]);
4920 if (_note[1] > _note[0]) {
4921 _drag_rect->set_x1 (x);
4923 _drag_rect->set_x0 (x);
4928 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4930 if (!had_movement) {
4934 framepos_t const start = min (_note[0], _note[1]);
4935 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
4937 framecnt_t const g = grid_frames (start);
4938 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4940 if (_editor->snap_mode() == SnapNormal && length < g) {
4941 length = g - one_tick;
4944 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4946 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
4950 NoteCreateDrag::y_to_region (double y) const
4953 _region_view->get_canvas_group()->canvas_to_item (x, y);
4958 NoteCreateDrag::aborted (bool)
4963 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4968 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
4972 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4974 Drag::start_grab (event, cursor);
4978 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4984 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4987 distance = _drags->current_pointer_x() - grab_x();
4988 len = ar->fade_in()->back()->when;
4990 distance = grab_x() - _drags->current_pointer_x();
4991 len = ar->fade_out()->back()->when;
4994 /* how long should it be ? */
4996 new_length = len + _editor->pixel_to_sample (distance);
4998 /* now check with the region that this is legal */
5000 new_length = ar->verify_xfade_bounds (new_length, start);
5003 arv->reset_fade_in_shape_width (ar, new_length);
5005 arv->reset_fade_out_shape_width (ar, new_length);
5010 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5016 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5019 distance = _drags->current_pointer_x() - grab_x();
5020 len = ar->fade_in()->back()->when;
5022 distance = grab_x() - _drags->current_pointer_x();
5023 len = ar->fade_out()->back()->when;
5026 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5028 _editor->begin_reversible_command ("xfade trim");
5029 ar->playlist()->clear_owned_changes ();
5032 ar->set_fade_in_length (new_length);
5034 ar->set_fade_out_length (new_length);
5037 /* Adjusting the xfade may affect other regions in the playlist, so we need
5038 to get undo Commands from the whole playlist rather than just the
5042 vector<Command*> cmds;
5043 ar->playlist()->rdiff (cmds);
5044 _editor->session()->add_commands (cmds);
5045 _editor->commit_reversible_command ();
5050 CrossfadeEdgeDrag::aborted (bool)
5053 arv->redraw_start_xfade ();
5055 arv->redraw_end_xfade ();