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::motion_handler (GdkEvent* e, bool from_autoscroll)
168 _current_pointer_frame = _editor->canvas_event_frame (e, &_current_pointer_x, &_current_pointer_y);
170 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
171 bool const t = (*i)->motion_handler (e, from_autoscroll);
182 DragManager::window_motion_handler (GdkEvent* e, bool from_autoscroll)
186 _current_pointer_frame = _editor->window_event_frame (e, &_current_pointer_x, &_current_pointer_y);
188 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
189 bool const t = (*i)->motion_handler (e, from_autoscroll);
200 DragManager::have_item (ArdourCanvas::Item* i) const
202 list<Drag*>::const_iterator j = _drags.begin ();
203 while (j != _drags.end() && (*j)->item () != i) {
207 return j != _drags.end ();
210 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
213 , _pointer_frame_offset (0)
214 , _move_threshold_passed (false)
215 , _raw_grab_frame (0)
217 , _last_pointer_frame (0)
223 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
236 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
238 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
240 if (Keyboard::is_button2_event (&event->button)) {
241 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
242 _y_constrained = true;
243 _x_constrained = false;
245 _y_constrained = false;
246 _x_constrained = true;
249 _x_constrained = false;
250 _y_constrained = false;
253 _raw_grab_frame = _editor->canvas_event_frame (event, &_grab_x, &_grab_y);
254 setup_pointer_frame_offset ();
255 _grab_frame = adjusted_frame (_raw_grab_frame, event);
256 _last_pointer_frame = _grab_frame;
257 _last_pointer_x = _grab_x;
258 _last_pointer_y = _grab_y;
264 /* CAIROCANVAS need a variant here that passes *cursor */
269 if (_editor->session() && _editor->session()->transport_rolling()) {
272 _was_rolling = false;
275 switch (_editor->snap_type()) {
276 case SnapToRegionStart:
277 case SnapToRegionEnd:
278 case SnapToRegionSync:
279 case SnapToRegionBoundary:
280 _editor->build_region_boundary_cache ();
287 /** Call to end a drag `successfully'. Ungrabs item and calls
288 * subclass' finished() method.
290 * @param event GDK event, or 0.
291 * @return true if some movement occurred, otherwise false.
294 Drag::end_grab (GdkEvent* event)
296 _editor->stop_canvas_autoscroll ();
300 finished (event, _move_threshold_passed);
302 _editor->verbose_cursor()->hide ();
304 return _move_threshold_passed;
308 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
312 if (f > _pointer_frame_offset) {
313 pos = f - _pointer_frame_offset;
317 _editor->snap_to_with_modifier (pos, event);
324 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
326 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
330 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
332 /* check to see if we have moved in any way that matters since the last motion event */
333 if (_move_threshold_passed &&
334 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
335 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
339 pair<framecnt_t, int> const threshold = move_threshold ();
341 bool const old_move_threshold_passed = _move_threshold_passed;
343 if (!from_autoscroll && !_move_threshold_passed) {
345 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
346 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
348 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
351 if (active (_editor->mouse_mode) && _move_threshold_passed) {
353 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
354 if (!from_autoscroll) {
355 bool const moving_left = _drags->current_pointer_x() < _last_pointer_x;
356 bool const moving_up = _drags->current_pointer_y() < _last_pointer_y;
357 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), moving_left, moving_up);
360 motion (event, _move_threshold_passed != old_move_threshold_passed);
362 _last_pointer_x = _drags->current_pointer_x ();
363 _last_pointer_y = _drags->current_pointer_y ();
364 _last_pointer_frame = adjusted_current_frame (event);
372 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
380 aborted (_move_threshold_passed);
382 _editor->stop_canvas_autoscroll ();
383 _editor->verbose_cursor()->hide ();
387 Drag::show_verbose_cursor_time (framepos_t frame)
389 _editor->verbose_cursor()->set_time (
391 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
392 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value()
395 _editor->verbose_cursor()->show ();
399 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
401 _editor->verbose_cursor()->show (xoffset);
403 _editor->verbose_cursor()->set_duration (
405 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
406 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value()
411 Drag::show_verbose_cursor_text (string const & text)
413 _editor->verbose_cursor()->show ();
415 _editor->verbose_cursor()->set (
417 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
418 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value()
422 boost::shared_ptr<Region>
423 Drag::add_midi_region (MidiTimeAxisView* view)
425 if (_editor->session()) {
426 const TempoMap& map (_editor->session()->tempo_map());
427 framecnt_t pos = grab_frame();
428 const Meter& m = map.meter_at (pos);
429 /* not that the frame rate used here can be affected by pull up/down which
432 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
433 return view->add_region (grab_frame(), len, true);
436 return boost::shared_ptr<Region>();
439 struct EditorOrderTimeAxisViewSorter {
440 bool operator() (TimeAxisView* a, TimeAxisView* b) {
441 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
442 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
444 return ra->route()->order_key (EditorSort) < rb->route()->order_key (EditorSort);
448 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
452 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
454 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
455 as some of the regions we are dragging may be on such tracks.
458 TrackViewList track_views = _editor->track_views;
459 track_views.sort (EditorOrderTimeAxisViewSorter ());
461 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
462 _time_axis_views.push_back (*i);
464 TimeAxisView::Children children_list = (*i)->get_child_list ();
465 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
466 _time_axis_views.push_back (j->get());
470 /* the list of views can be empty at this point if this is a region list-insert drag
473 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
474 _views.push_back (DraggingView (*i, this));
477 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
481 RegionDrag::region_going_away (RegionView* v)
483 list<DraggingView>::iterator i = _views.begin ();
484 while (i != _views.end() && i->view != v) {
488 if (i != _views.end()) {
493 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
494 * or -1 if it is not found.
497 RegionDrag::find_time_axis_view (TimeAxisView* t) const
500 int const N = _time_axis_views.size ();
501 while (i < N && _time_axis_views[i] != t) {
512 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
513 : RegionDrag (e, i, p, v),
517 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
521 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
523 Drag::start_grab (event, cursor);
525 show_verbose_cursor_time (_last_frame_position);
527 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
528 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
529 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
533 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
535 /* compute the amount of pointer motion in frames, and where
536 the region would be if we moved it by that much.
538 *pending_region_position = adjusted_current_frame (event);
540 framepos_t sync_frame;
541 framecnt_t sync_offset;
544 sync_offset = _primary->region()->sync_offset (sync_dir);
546 /* we don't handle a sync point that lies before zero.
548 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
550 sync_frame = *pending_region_position + (sync_dir*sync_offset);
552 _editor->snap_to_with_modifier (sync_frame, event);
554 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
557 *pending_region_position = _last_frame_position;
560 if (*pending_region_position > max_framepos - _primary->region()->length()) {
561 *pending_region_position = _last_frame_position;
566 /* in locked edit mode, reverse the usual meaning of _x_constrained */
567 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
569 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
571 /* x movement since last time (in pixels) */
572 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
574 /* total x movement */
575 framecnt_t total_dx = *pending_region_position;
576 if (regions_came_from_canvas()) {
577 total_dx = total_dx - grab_frame ();
580 /* check that no regions have gone off the start of the session */
581 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
582 if ((i->view->region()->position() + total_dx) < 0) {
584 *pending_region_position = _last_frame_position;
589 _last_frame_position = *pending_region_position;
596 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
598 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
599 int const n = i->time_axis_view + delta_track;
600 if (n < 0 || n >= int (_time_axis_views.size())) {
601 /* off the top or bottom track */
605 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
606 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
607 /* not a track, or the wrong type */
611 double const l = i->layer + delta_layer;
613 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
614 mode to allow the user to place a region below another on layer 0.
616 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
617 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
618 If it has, the layers will be munged later anyway, so it's ok.
624 /* all regions being dragged are ok with this change */
629 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
631 assert (!_views.empty ());
633 /* Find the TimeAxisView that the pointer is now over */
634 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
636 if (first_move && tv.first->view()->layer_display() == Stacked) {
637 tv.first->view()->set_layer_display (Expanded);
640 /* Bail early if we're not over a track */
641 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
642 if (!rtv || !rtv->is_track()) {
643 _editor->verbose_cursor()->hide ();
647 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
649 /* Here's the current pointer position in terms of time axis view and layer */
650 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
651 double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
653 /* Work out the change in x */
654 framepos_t pending_region_position;
655 double const x_delta = compute_x_delta (event, &pending_region_position);
657 /* Work out the change in y */
659 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
660 double delta_layer = current_pointer_layer - _last_pointer_layer;
662 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
663 /* this y movement is not allowed, so do no y movement this time */
664 delta_time_axis_view = 0;
668 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
669 /* haven't reached next snap point, and we're not switching
670 trackviews nor layers. nothing to do.
675 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
677 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
679 RegionView* rv = i->view;
681 if (rv->region()->locked() || rv->region()->video_locked()) {
689 /* Reparent to a non scrolling group so that we can keep the
690 region selection above all time axis views.
691 Reparenting means that we will have to move the region view
692 within its new parent, as the two parent groups have different coordinates.
695 ArdourCanvas::Group* rvg = rv->get_canvas_group();
696 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
698 rv->get_canvas_group()->reparent (_editor->_region_motion_group);
700 rv->fake_set_opaque (true);
701 rvg->set_position (rv_canvas_offset);
704 /* If we have moved tracks, we'll fudge the layer delta so that the
705 region gets moved back onto layer 0 on its new track; this avoids
706 confusion when dragging regions from non-zero layers onto different
709 double this_delta_layer = delta_layer;
710 if (delta_time_axis_view != 0) {
711 this_delta_layer = - i->layer;
714 /* The TimeAxisView that this region is now on */
715 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
717 /* Ensure it is moved from stacked -> expanded if appropriate */
718 if (tv->view()->layer_display() == Stacked) {
719 tv->view()->set_layer_display (Expanded);
722 /* We're only allowed to go -ve in layer on Expanded views */
723 if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
724 this_delta_layer = - i->layer;
728 rv->set_height (tv->view()->child_height ());
730 /* Update show/hidden status as the region view may have come from a hidden track,
731 or have moved to one.
734 rv->get_canvas_group()->hide ();
736 rv->get_canvas_group()->show ();
739 /* Update the DraggingView */
740 i->time_axis_view += delta_time_axis_view;
741 i->layer += this_delta_layer;
744 _editor->mouse_brush_insert_region (rv, pending_region_position);
749 /* Get the y coordinate of the top of the track that this region is now on */
750 tv->canvas_display()->item_to_canvas (x, y);
752 /* And adjust for the layer that it should be on */
753 StreamView* cv = tv->view ();
754 switch (cv->layer_display ()) {
758 y += (cv->layers() - i->layer - 1) * cv->child_height ();
761 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
765 /* Now move the region view */
766 rv->move (x_delta, y - rv->get_canvas_group()->position().y);
769 } /* foreach region */
771 _total_x_delta += x_delta;
773 if (x_delta != 0 && !_brushing) {
774 show_verbose_cursor_time (_last_frame_position);
777 _last_pointer_time_axis_view += delta_time_axis_view;
778 _last_pointer_layer += delta_layer;
782 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
784 if (_copy && first_move) {
786 /* duplicate the regionview(s) and region(s) */
788 list<DraggingView> new_regionviews;
790 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
792 RegionView* rv = i->view;
793 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
794 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
796 const boost::shared_ptr<const Region> original = rv->region();
797 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
798 region_copy->set_position (original->position());
802 boost::shared_ptr<AudioRegion> audioregion_copy
803 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
805 nrv = new AudioRegionView (*arv, audioregion_copy);
807 boost::shared_ptr<MidiRegion> midiregion_copy
808 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
809 nrv = new MidiRegionView (*mrv, midiregion_copy);
814 nrv->get_canvas_group()->show ();
815 new_regionviews.push_back (DraggingView (nrv, this));
817 /* swap _primary to the copy */
819 if (rv == _primary) {
823 /* ..and deselect the one we copied */
825 rv->set_selected (false);
828 if (!new_regionviews.empty()) {
830 /* reflect the fact that we are dragging the copies */
832 _views = new_regionviews;
834 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
838 RegionMotionDrag::motion (event, first_move);
842 RegionMotionDrag::finished (GdkEvent *, bool)
844 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
849 if ((*i)->view()->layer_display() == Expanded) {
850 (*i)->view()->set_layer_display (Stacked);
856 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
858 RegionMotionDrag::finished (ev, movement_occurred);
860 if (!movement_occurred) {
865 /* reverse this here so that we have the correct logic to finalize
869 if (Config->get_edit_mode() == Lock) {
870 _x_constrained = !_x_constrained;
873 assert (!_views.empty ());
875 /* We might have hidden region views so that they weren't visible during the drag
876 (when they have been reparented). Now everything can be shown again, as region
877 views are back in their track parent groups.
879 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
880 i->view->get_canvas_group()->show ();
883 bool const changed_position = (_last_frame_position != _primary->region()->position());
884 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
885 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
905 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
909 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
911 RegionSelection new_views;
912 PlaylistSet modified_playlists;
913 list<RegionView*> views_to_delete;
916 /* all changes were made during motion event handlers */
918 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
922 _editor->commit_reversible_command ();
926 if (_x_constrained) {
927 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
929 _editor->begin_reversible_command (Operations::region_copy);
932 /* insert the regions into their new playlists */
933 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
935 if (i->view->region()->locked() || i->view->region()->video_locked()) {
941 if (changed_position && !_x_constrained) {
942 where = i->view->region()->position() - drag_delta;
944 where = i->view->region()->position();
947 RegionView* new_view = insert_region_into_playlist (
948 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
955 new_views.push_back (new_view);
957 /* we don't need the copied RegionView any more */
958 views_to_delete.push_back (i->view);
961 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
962 because when views are deleted they are automagically removed from _views, which messes
965 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
969 /* If we've created new regions either by copying or moving
970 to a new track, we want to replace the old selection with the new ones
973 if (new_views.size() > 0) {
974 _editor->selection->set (new_views);
977 /* write commands for the accumulated diffs for all our modified playlists */
978 add_stateful_diff_commands_for_playlists (modified_playlists);
980 _editor->commit_reversible_command ();
984 RegionMoveDrag::finished_no_copy (
985 bool const changed_position,
986 bool const changed_tracks,
987 framecnt_t const drag_delta
990 RegionSelection new_views;
991 PlaylistSet modified_playlists;
992 PlaylistSet frozen_playlists;
993 set<RouteTimeAxisView*> views_to_update;
996 /* all changes were made during motion event handlers */
997 _editor->commit_reversible_command ();
1001 if (_x_constrained) {
1002 _editor->begin_reversible_command (_("fixed time region drag"));
1004 _editor->begin_reversible_command (Operations::region_drag);
1007 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1009 RegionView* rv = i->view;
1011 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1012 double const dest_layer = i->layer;
1014 if (rv->region()->locked() || rv->region()->video_locked()) {
1019 views_to_update.insert (dest_rtv);
1023 if (changed_position && !_x_constrained) {
1024 where = rv->region()->position() - drag_delta;
1026 where = rv->region()->position();
1029 if (changed_tracks) {
1031 /* insert into new playlist */
1033 RegionView* new_view = insert_region_into_playlist (
1034 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1037 if (new_view == 0) {
1042 new_views.push_back (new_view);
1044 /* remove from old playlist */
1046 /* the region that used to be in the old playlist is not
1047 moved to the new one - we use a copy of it. as a result,
1048 any existing editor for the region should no longer be
1051 rv->hide_region_editor();
1052 rv->fake_set_opaque (false);
1054 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1058 rv->region()->clear_changes ();
1061 motion on the same track. plonk the previously reparented region
1062 back to its original canvas group (its streamview).
1063 No need to do anything for copies as they are fake regions which will be deleted.
1066 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1067 rv->get_canvas_group()->set_y_position (i->initial_y);
1070 /* just change the model */
1072 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1074 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1075 playlist->set_layer (rv->region(), dest_layer);
1078 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1080 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1083 playlist->freeze ();
1086 /* this movement may result in a crossfade being modified, so we need to get undo
1087 data from the playlist as well as the region.
1090 r = modified_playlists.insert (playlist);
1092 playlist->clear_changes ();
1095 rv->region()->set_position (where);
1097 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1100 if (changed_tracks) {
1102 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1103 was selected in all of them, then removing it from a playlist will have removed all
1104 trace of it from _views (i.e. there were N regions selected, we removed 1,
1105 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1106 corresponding regionview, and _views is now empty).
1108 This could have invalidated any and all iterators into _views.
1110 The heuristic we use here is: if the region selection is empty, break out of the loop
1111 here. if the region selection is not empty, then restart the loop because we know that
1112 we must have removed at least the region(view) we've just been working on as well as any
1113 that we processed on previous iterations.
1115 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1116 we can just iterate.
1120 if (_views.empty()) {
1131 /* If we've created new regions either by copying or moving
1132 to a new track, we want to replace the old selection with the new ones
1135 if (new_views.size() > 0) {
1136 _editor->selection->set (new_views);
1139 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1143 /* write commands for the accumulated diffs for all our modified playlists */
1144 add_stateful_diff_commands_for_playlists (modified_playlists);
1146 _editor->commit_reversible_command ();
1148 /* We have futzed with the layering of canvas items on our streamviews.
1149 If any region changed layer, this will have resulted in the stream
1150 views being asked to set up their region views, and all will be well.
1151 If not, we might now have badly-ordered region views. Ask the StreamViews
1152 involved to sort themselves out, just in case.
1155 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1156 (*i)->view()->playlist_layered ((*i)->track ());
1160 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1161 * @param region Region to remove.
1162 * @param playlist playlist To remove from.
1163 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1164 * that clear_changes () is only called once per playlist.
1167 RegionMoveDrag::remove_region_from_playlist (
1168 boost::shared_ptr<Region> region,
1169 boost::shared_ptr<Playlist> playlist,
1170 PlaylistSet& modified_playlists
1173 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1176 playlist->clear_changes ();
1179 playlist->remove_region (region);
1183 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1184 * clearing the playlist's diff history first if necessary.
1185 * @param region Region to insert.
1186 * @param dest_rtv Destination RouteTimeAxisView.
1187 * @param dest_layer Destination layer.
1188 * @param where Destination position.
1189 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1190 * that clear_changes () is only called once per playlist.
1191 * @return New RegionView, or 0 if no insert was performed.
1194 RegionMoveDrag::insert_region_into_playlist (
1195 boost::shared_ptr<Region> region,
1196 RouteTimeAxisView* dest_rtv,
1199 PlaylistSet& modified_playlists
1202 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1203 if (!dest_playlist) {
1207 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1208 _new_region_view = 0;
1209 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1211 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1212 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1214 dest_playlist->clear_changes ();
1217 dest_playlist->add_region (region, where);
1219 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1220 dest_playlist->set_layer (region, dest_layer);
1225 assert (_new_region_view);
1227 return _new_region_view;
1231 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1233 _new_region_view = rv;
1237 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1239 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1240 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1242 _editor->session()->add_command (c);
1251 RegionMoveDrag::aborted (bool movement_occurred)
1255 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1262 RegionMotionDrag::aborted (movement_occurred);
1267 RegionMotionDrag::aborted (bool)
1269 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1270 if ((*i)->view()->layer_display() == Expanded) {
1271 (*i)->view()->set_layer_display (Stacked);
1275 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1276 RegionView* rv = i->view;
1277 TimeAxisView* tv = &(rv->get_time_axis_view ());
1278 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1280 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1281 rv->get_canvas_group()->set_y_position (0);
1283 rv->fake_set_opaque (false);
1284 rv->move (-_total_x_delta, 0);
1285 rv->set_height (rtv->view()->child_height ());
1289 /** @param b true to brush, otherwise false.
1290 * @param c true to make copies of the regions being moved, otherwise false.
1292 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1293 : RegionMotionDrag (e, i, p, v, b),
1296 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1299 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1300 if (rtv && rtv->is_track()) {
1301 speed = rtv->track()->speed ();
1304 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1308 RegionMoveDrag::setup_pointer_frame_offset ()
1310 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1313 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1314 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1316 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1318 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1319 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1321 _primary = v->view()->create_region_view (r, false, false);
1323 _primary->get_canvas_group()->show ();
1324 _primary->set_position (pos, 0);
1325 _views.push_back (DraggingView (_primary, this));
1327 _last_frame_position = pos;
1329 _item = _primary->get_canvas_group ();
1333 RegionInsertDrag::finished (GdkEvent *, bool)
1335 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1337 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1338 _primary->get_canvas_group()->set_y_position (0);
1340 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1342 _editor->begin_reversible_command (Operations::insert_region);
1343 playlist->clear_changes ();
1344 playlist->add_region (_primary->region (), _last_frame_position);
1345 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1346 _editor->commit_reversible_command ();
1354 RegionInsertDrag::aborted (bool)
1361 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1362 : RegionMoveDrag (e, i, p, v, false, false)
1364 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1367 struct RegionSelectionByPosition {
1368 bool operator() (RegionView*a, RegionView* b) {
1369 return a->region()->position () < b->region()->position();
1374 RegionSpliceDrag::motion (GdkEvent* event, bool)
1376 /* Which trackview is this ? */
1378 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1379 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1381 /* The region motion is only processed if the pointer is over
1385 if (!tv || !tv->is_track()) {
1386 /* To make sure we hide the verbose canvas cursor when the mouse is
1387 not held over and audiotrack.
1389 _editor->verbose_cursor()->hide ();
1395 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1401 RegionSelection copy (_editor->selection->regions);
1403 RegionSelectionByPosition cmp;
1406 framepos_t const pf = adjusted_current_frame (event);
1408 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1410 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1416 boost::shared_ptr<Playlist> playlist;
1418 if ((playlist = atv->playlist()) == 0) {
1422 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1427 if (pf < (*i)->region()->last_frame() + 1) {
1431 if (pf > (*i)->region()->first_frame()) {
1437 playlist->shuffle ((*i)->region(), dir);
1442 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1444 RegionMoveDrag::finished (event, movement_occurred);
1448 RegionSpliceDrag::aborted (bool)
1453 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1455 _view (dynamic_cast<MidiTimeAxisView*> (v))
1457 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1463 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1466 _region = add_midi_region (_view);
1467 _view->playlist()->freeze ();
1470 framepos_t const f = adjusted_current_frame (event);
1471 if (f < grab_frame()) {
1472 _region->set_position (f);
1475 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1476 so that if this region is duplicated, its duplicate starts on
1477 a snap point rather than 1 frame after a snap point. Otherwise things get
1478 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1479 place snapped notes at the start of the region.
1482 framecnt_t const len = (framecnt_t) fabs (f - grab_frame () - 1);
1483 _region->set_length (len < 1 ? 1 : len);
1489 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1491 if (!movement_occurred) {
1492 add_midi_region (_view);
1494 _view->playlist()->thaw ();
1499 RegionCreateDrag::aborted (bool)
1502 _view->playlist()->thaw ();
1508 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1512 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1516 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1518 Gdk::Cursor* cursor;
1519 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1521 float x_fraction = cnote->mouse_x_fraction ();
1523 if (x_fraction > 0.0 && x_fraction < 0.25) {
1524 cursor = _editor->cursors()->left_side_trim;
1526 cursor = _editor->cursors()->right_side_trim;
1529 Drag::start_grab (event, cursor);
1531 region = &cnote->region_view();
1533 double const region_start = region->get_position_pixels();
1534 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1536 if (grab_x() <= middle_point) {
1537 cursor = _editor->cursors()->left_side_trim;
1540 cursor = _editor->cursors()->right_side_trim;
1546 if (event->motion.state & Keyboard::PrimaryModifier) {
1552 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1554 if (ms.size() > 1) {
1555 /* has to be relative, may make no sense otherwise */
1559 /* select this note; if it is already selected, preserve the existing selection,
1560 otherwise make this note the only one selected.
1562 region->note_selected (cnote, cnote->selected ());
1564 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1565 MidiRegionSelection::iterator next;
1568 (*r)->begin_resizing (at_front);
1574 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1576 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1577 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1578 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1580 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1585 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1587 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1588 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1589 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1591 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1596 NoteResizeDrag::aborted (bool)
1598 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1599 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1600 (*r)->abort_resizing ();
1604 AVDraggingView::AVDraggingView (RegionView* v)
1607 initial_position = v->region()->position ();
1610 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1613 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1616 TrackViewList empty;
1618 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1619 std::list<RegionView*> views = rs.by_layer();
1621 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1622 RegionView* rv = (*i);
1623 if (!rv->region()->video_locked()) {
1626 _views.push_back (AVDraggingView (rv));
1631 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1633 Drag::start_grab (event);
1634 if (_editor->session() == 0) {
1638 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1639 _max_backwards_drag = (
1640 ARDOUR_UI::instance()->video_timeline->get_duration()
1641 + ARDOUR_UI::instance()->video_timeline->get_offset()
1642 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1645 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1646 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1647 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1650 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1653 Timecode::Time timecode;
1654 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1655 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);
1656 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1657 _editor->verbose_cursor()->show ();
1661 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1663 if (_editor->session() == 0) {
1666 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1670 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1671 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(dt);
1673 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1674 dt = - _max_backwards_drag;
1677 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1678 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1680 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1681 RegionView* rv = i->view;
1682 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1685 rv->fake_set_opaque (true);
1686 rv->region()->clear_changes ();
1687 rv->region()->suspend_property_changes();
1689 rv->region()->set_position(i->initial_position + dt);
1690 rv->region_changed(ARDOUR::Properties::position);
1693 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1694 Timecode::Time timecode;
1695 Timecode::Time timediff;
1697 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1698 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1699 snprintf (buf, sizeof (buf),
1700 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1701 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1702 , _("Video Start:"),
1703 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1705 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1707 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1708 _editor->verbose_cursor()->show ();
1712 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1714 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1718 if (!movement_occurred || ! _editor->session()) {
1722 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1724 _editor->begin_reversible_command (_("Move Video"));
1726 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1727 ARDOUR_UI::instance()->video_timeline->save_undo();
1728 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1729 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1731 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1732 i->view->drag_end();
1733 i->view->fake_set_opaque (false);
1734 i->view->region()->resume_property_changes ();
1736 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1739 _editor->session()->maybe_update_session_range(
1740 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1741 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1745 _editor->commit_reversible_command ();
1749 VideoTimeLineDrag::aborted (bool)
1751 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1754 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1755 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1757 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1758 i->view->region()->resume_property_changes ();
1759 i->view->region()->set_position(i->initial_position);
1763 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1764 : RegionDrag (e, i, p, v)
1766 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1767 _preserve_fade_anchor = preserve_fade_anchor;
1771 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1774 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1775 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1777 if (tv && tv->is_track()) {
1778 speed = tv->track()->speed();
1781 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1782 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1783 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1785 framepos_t const pf = adjusted_current_frame (event);
1787 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1788 /* Move the contents of the region around without changing the region bounds */
1789 _operation = ContentsTrim;
1790 Drag::start_grab (event, _editor->cursors()->trimmer);
1792 /* These will get overridden for a point trim.*/
1793 if (pf < (region_start + region_length/2)) {
1794 /* closer to front */
1795 _operation = StartTrim;
1796 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1799 _operation = EndTrim;
1800 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1804 switch (_operation) {
1806 show_verbose_cursor_time (region_start);
1807 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1808 i->view->trim_front_starting ();
1812 show_verbose_cursor_time (region_end);
1815 show_verbose_cursor_time (pf);
1819 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1820 i->view->region()->suspend_property_changes ();
1825 TrimDrag::motion (GdkEvent* event, bool first_move)
1827 RegionView* rv = _primary;
1830 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1831 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1832 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1833 frameoffset_t frame_delta = 0;
1835 if (tv && tv->is_track()) {
1836 speed = tv->track()->speed();
1839 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1845 switch (_operation) {
1847 trim_type = "Region start trim";
1850 trim_type = "Region end trim";
1853 trim_type = "Region content trim";
1857 _editor->begin_reversible_command (trim_type);
1859 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1860 RegionView* rv = i->view;
1861 rv->fake_set_opaque (false);
1862 rv->enable_display (false);
1863 rv->region()->playlist()->clear_owned_changes ();
1865 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1868 arv->temporarily_hide_envelope ();
1872 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1873 insert_result = _editor->motion_frozen_playlists.insert (pl);
1875 if (insert_result.second) {
1881 bool non_overlap_trim = false;
1883 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1884 non_overlap_trim = true;
1887 switch (_operation) {
1889 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1890 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1891 if (changed && _preserve_fade_anchor) {
1892 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1897 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1898 distance = _drags->current_pointer_x() - grab_x();
1899 len = ar->fade_in()->back()->when;
1900 new_length = len - _editor->pixel_to_sample (distance);
1901 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
1902 arv->reset_fade_in_shape_width (ar, new_length); //the grey shape
1909 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1910 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1911 if (changed && _preserve_fade_anchor) {
1912 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1917 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1918 distance = grab_x() - _drags->current_pointer_x();
1919 len = ar->fade_out()->back()->when;
1920 new_length = len - _editor->pixel_to_sample (distance);
1921 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
1922 arv->reset_fade_out_shape_width (ar, new_length); //the grey shape
1930 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1931 // frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1933 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1934 i->view->move_contents (frame_delta);
1940 switch (_operation) {
1942 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1945 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1948 // show_verbose_cursor_time (frame_delta);
1955 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1957 if (movement_occurred) {
1958 motion (event, false);
1960 if (_operation == StartTrim) {
1961 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1963 /* This must happen before the region's StatefulDiffCommand is created, as it may
1964 `correct' (ahem) the region's _start from being negative to being zero. It
1965 needs to be zero in the undo record.
1967 i->view->trim_front_ending ();
1969 if (_preserve_fade_anchor) {
1970 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1975 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1976 distance = _drags->current_pointer_x() - grab_x();
1977 len = ar->fade_in()->back()->when;
1978 new_length = len - _editor->pixel_to_sample (distance);
1979 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
1980 ar->set_fade_in_length(new_length);
1984 } else if (_operation == EndTrim) {
1985 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1986 if (_preserve_fade_anchor) {
1987 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1992 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1993 distance = _drags->current_pointer_x() - grab_x();
1994 len = ar->fade_out()->back()->when;
1995 new_length = len - _editor->pixel_to_sample (distance);
1996 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
1997 ar->set_fade_out_length(new_length);
2003 if (!_views.empty()) {
2004 if (_operation == StartTrim) {
2005 _editor->maybe_locate_with_edit_preroll(
2006 _views.begin()->view->region()->position());
2008 if (_operation == EndTrim) {
2009 _editor->maybe_locate_with_edit_preroll(
2010 _views.begin()->view->region()->position() +
2011 _views.begin()->view->region()->length());
2015 if (!_editor->selection->selected (_primary)) {
2016 _primary->thaw_after_trim ();
2019 set<boost::shared_ptr<Playlist> > diffed_playlists;
2021 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2022 i->view->thaw_after_trim ();
2023 i->view->enable_display (true);
2024 i->view->fake_set_opaque (true);
2026 /* Trimming one region may affect others on the playlist, so we need
2027 to get undo Commands from the whole playlist rather than just the
2028 region. Use diffed_playlists to make sure we don't diff a given
2029 playlist more than once.
2031 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2032 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2033 vector<Command*> cmds;
2035 _editor->session()->add_commands (cmds);
2036 diffed_playlists.insert (p);
2041 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2045 _editor->motion_frozen_playlists.clear ();
2046 _editor->commit_reversible_command();
2049 /* no mouse movement */
2050 _editor->point_trim (event, adjusted_current_frame (event));
2053 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2054 if (_operation == StartTrim) {
2055 i->view->trim_front_ending ();
2058 i->view->region()->resume_property_changes ();
2063 TrimDrag::aborted (bool movement_occurred)
2065 /* Our motion method is changing model state, so use the Undo system
2066 to cancel. Perhaps not ideal, as this will leave an Undo point
2067 behind which may be slightly odd from the user's point of view.
2072 if (movement_occurred) {
2076 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2077 i->view->region()->resume_property_changes ();
2082 TrimDrag::setup_pointer_frame_offset ()
2084 list<DraggingView>::iterator i = _views.begin ();
2085 while (i != _views.end() && i->view != _primary) {
2089 if (i == _views.end()) {
2093 switch (_operation) {
2095 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2098 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2105 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2109 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2110 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2115 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2117 Drag::start_grab (event, cursor);
2118 show_verbose_cursor_time (adjusted_current_frame(event));
2122 MeterMarkerDrag::setup_pointer_frame_offset ()
2124 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2128 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2132 // create a dummy marker for visual representation of moving the
2133 // section, because whether its a copy or not, we're going to
2134 // leave or lose the original marker (leave if its a copy; lose if its
2135 // not, because we'll remove it from the map).
2137 MeterSection section (_marker->meter());
2139 if (!section.movable()) {
2144 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2146 _marker = new MeterMarker (
2148 *_editor->meter_group,
2149 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2151 *new MeterSection (_marker->meter())
2154 /* use the new marker for the grab */
2155 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2158 TempoMap& map (_editor->session()->tempo_map());
2159 /* get current state */
2160 before_state = &map.get_state();
2161 /* remove the section while we drag it */
2162 map.remove_meter (section, true);
2166 framepos_t const pf = adjusted_current_frame (event);
2167 _marker->set_position (pf);
2168 show_verbose_cursor_time (pf);
2172 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2174 if (!movement_occurred) {
2178 motion (event, false);
2180 Timecode::BBT_Time when;
2182 TempoMap& map (_editor->session()->tempo_map());
2183 map.bbt_time (last_pointer_frame(), when);
2185 if (_copy == true) {
2186 _editor->begin_reversible_command (_("copy meter mark"));
2187 XMLNode &before = map.get_state();
2188 map.add_meter (_marker->meter(), when);
2189 XMLNode &after = map.get_state();
2190 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2191 _editor->commit_reversible_command ();
2194 _editor->begin_reversible_command (_("move meter mark"));
2196 /* we removed it before, so add it back now */
2198 map.add_meter (_marker->meter(), when);
2199 XMLNode &after = map.get_state();
2200 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2201 _editor->commit_reversible_command ();
2204 // delete the dummy marker we used for visual representation while moving.
2205 // a new visual marker will show up automatically.
2210 MeterMarkerDrag::aborted (bool moved)
2212 _marker->set_position (_marker->meter().frame ());
2215 TempoMap& map (_editor->session()->tempo_map());
2216 /* we removed it before, so add it back now */
2217 map.add_meter (_marker->meter(), _marker->meter().frame());
2218 // delete the dummy marker we used for visual representation while moving.
2219 // a new visual marker will show up automatically.
2224 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2228 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2230 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2235 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2237 Drag::start_grab (event, cursor);
2238 show_verbose_cursor_time (adjusted_current_frame (event));
2242 TempoMarkerDrag::setup_pointer_frame_offset ()
2244 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2248 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2252 // create a dummy marker for visual representation of moving the
2253 // section, because whether its a copy or not, we're going to
2254 // leave or lose the original marker (leave if its a copy; lose if its
2255 // not, because we'll remove it from the map).
2257 // create a dummy marker for visual representation of moving the copy.
2258 // The actual copying is not done before we reach the finish callback.
2261 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2263 TempoSection section (_marker->tempo());
2265 _marker = new TempoMarker (
2267 *_editor->tempo_group,
2268 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2270 *new TempoSection (_marker->tempo())
2273 /* use the new marker for the grab */
2274 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2277 TempoMap& map (_editor->session()->tempo_map());
2278 /* get current state */
2279 before_state = &map.get_state();
2280 /* remove the section while we drag it */
2281 map.remove_tempo (section, true);
2285 framepos_t const pf = adjusted_current_frame (event);
2286 _marker->set_position (pf);
2287 show_verbose_cursor_time (pf);
2291 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2293 if (!movement_occurred) {
2297 motion (event, false);
2299 TempoMap& map (_editor->session()->tempo_map());
2300 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2301 Timecode::BBT_Time when;
2303 map.bbt_time (beat_time, when);
2305 if (_copy == true) {
2306 _editor->begin_reversible_command (_("copy tempo mark"));
2307 XMLNode &before = map.get_state();
2308 map.add_tempo (_marker->tempo(), when);
2309 XMLNode &after = map.get_state();
2310 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2311 _editor->commit_reversible_command ();
2314 _editor->begin_reversible_command (_("move tempo mark"));
2315 /* we removed it before, so add it back now */
2316 map.add_tempo (_marker->tempo(), when);
2317 XMLNode &after = map.get_state();
2318 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2319 _editor->commit_reversible_command ();
2322 // delete the dummy marker we used for visual representation while moving.
2323 // a new visual marker will show up automatically.
2328 TempoMarkerDrag::aborted (bool moved)
2330 _marker->set_position (_marker->tempo().frame());
2332 TempoMap& map (_editor->session()->tempo_map());
2333 /* we removed it before, so add it back now */
2334 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2335 // delete the dummy marker we used for visual representation while moving.
2336 // a new visual marker will show up automatically.
2341 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2342 : Drag (e, &c.time_bar_canvas_item())
2346 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2349 /** Do all the things we do when dragging the playhead to make it look as though
2350 * we have located, without actually doing the locate (because that would cause
2351 * the diskstream buffers to be refilled, which is too slow).
2354 CursorDrag::fake_locate (framepos_t t)
2356 _editor->playhead_cursor->set_position (t);
2358 Session* s = _editor->session ();
2359 if (s->timecode_transmission_suspended ()) {
2360 framepos_t const f = _editor->playhead_cursor->current_frame ();
2361 s->send_mmc_locate (f);
2362 s->send_full_time_code (f);
2365 show_verbose_cursor_time (t);
2366 _editor->UpdateAllTransportClocks (t);
2370 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2372 Drag::start_grab (event, c);
2374 _grab_zoom = _editor->samples_per_pixel;
2376 framepos_t where = _editor->canvas_event_frame (event, 0, 0);
2378 _editor->snap_to_with_modifier (where, event);
2380 _editor->_dragging_playhead = true;
2382 Session* s = _editor->session ();
2384 /* grab the track canvas item as well */
2386 _cursor.track_canvas_item().grab();
2389 if (_was_rolling && _stop) {
2393 if (s->is_auditioning()) {
2394 s->cancel_audition ();
2398 if (AudioEngine::instance()->connected()) {
2400 /* do this only if we're the engine is connected
2401 * because otherwise this request will never be
2402 * serviced and we'll busy wait forever. likewise,
2403 * notice if we are disconnected while waiting for the
2404 * request to be serviced.
2407 s->request_suspend_timecode_transmission ();
2408 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2409 /* twiddle our thumbs */
2414 fake_locate (where);
2418 CursorDrag::motion (GdkEvent* event, bool)
2420 framepos_t const adjusted_frame = adjusted_current_frame (event);
2421 if (adjusted_frame != last_pointer_frame()) {
2422 fake_locate (adjusted_frame);
2427 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2429 _editor->_dragging_playhead = false;
2431 _cursor.track_canvas_item().ungrab();
2433 if (!movement_occurred && _stop) {
2437 motion (event, false);
2439 Session* s = _editor->session ();
2441 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2442 _editor->_pending_locate_request = true;
2443 s->request_resume_timecode_transmission ();
2448 CursorDrag::aborted (bool)
2450 _cursor.track_canvas_item().ungrab();
2452 if (_editor->_dragging_playhead) {
2453 _editor->session()->request_resume_timecode_transmission ();
2454 _editor->_dragging_playhead = false;
2457 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2460 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2461 : RegionDrag (e, i, p, v)
2463 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2467 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2469 Drag::start_grab (event, cursor);
2471 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2472 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2474 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2478 FadeInDrag::setup_pointer_frame_offset ()
2480 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2481 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2482 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2486 FadeInDrag::motion (GdkEvent* event, bool)
2488 framecnt_t fade_length;
2490 framepos_t const pos = adjusted_current_frame (event);
2492 boost::shared_ptr<Region> region = _primary->region ();
2494 if (pos < (region->position() + 64)) {
2495 fade_length = 64; // this should be a minimum defined somewhere
2496 } else if (pos > region->last_frame()) {
2497 fade_length = region->length();
2499 fade_length = pos - region->position();
2502 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2504 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2510 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2513 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2517 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2519 if (!movement_occurred) {
2523 framecnt_t fade_length;
2525 framepos_t const pos = adjusted_current_frame (event);
2527 boost::shared_ptr<Region> region = _primary->region ();
2529 if (pos < (region->position() + 64)) {
2530 fade_length = 64; // this should be a minimum defined somewhere
2531 } else if (pos > region->last_frame()) {
2532 fade_length = region->length();
2534 fade_length = pos - region->position();
2537 _editor->begin_reversible_command (_("change fade in length"));
2539 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2541 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2547 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2548 XMLNode &before = alist->get_state();
2550 tmp->audio_region()->set_fade_in_length (fade_length);
2551 tmp->audio_region()->set_fade_in_active (true);
2553 XMLNode &after = alist->get_state();
2554 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2557 _editor->commit_reversible_command ();
2561 FadeInDrag::aborted (bool)
2563 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2564 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2570 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2574 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2575 : RegionDrag (e, i, p, v)
2577 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2581 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2583 Drag::start_grab (event, cursor);
2585 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2586 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2588 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2592 FadeOutDrag::setup_pointer_frame_offset ()
2594 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2595 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2596 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2600 FadeOutDrag::motion (GdkEvent* event, bool)
2602 framecnt_t fade_length;
2604 framepos_t const pos = adjusted_current_frame (event);
2606 boost::shared_ptr<Region> region = _primary->region ();
2608 if (pos > (region->last_frame() - 64)) {
2609 fade_length = 64; // this should really be a minimum fade defined somewhere
2611 else if (pos < region->position()) {
2612 fade_length = region->length();
2615 fade_length = region->last_frame() - pos;
2618 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2620 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2626 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2629 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2633 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2635 if (!movement_occurred) {
2639 framecnt_t fade_length;
2641 framepos_t const pos = adjusted_current_frame (event);
2643 boost::shared_ptr<Region> region = _primary->region ();
2645 if (pos > (region->last_frame() - 64)) {
2646 fade_length = 64; // this should really be a minimum fade defined somewhere
2648 else if (pos < region->position()) {
2649 fade_length = region->length();
2652 fade_length = region->last_frame() - pos;
2655 _editor->begin_reversible_command (_("change fade out length"));
2657 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2659 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2665 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2666 XMLNode &before = alist->get_state();
2668 tmp->audio_region()->set_fade_out_length (fade_length);
2669 tmp->audio_region()->set_fade_out_active (true);
2671 XMLNode &after = alist->get_state();
2672 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2675 _editor->commit_reversible_command ();
2679 FadeOutDrag::aborted (bool)
2681 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2682 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2688 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2692 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2695 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2697 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2700 _points.push_back (ArdourCanvas::Duple (0, 0));
2701 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2704 MarkerDrag::~MarkerDrag ()
2706 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2711 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2713 location = new Location (*l);
2714 markers.push_back (m);
2719 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2721 Drag::start_grab (event, cursor);
2725 Location *location = _editor->find_location_from_marker (_marker, is_start);
2726 _editor->_dragging_edit_point = true;
2728 update_item (location);
2730 // _drag_line->show();
2731 // _line->raise_to_top();
2734 show_verbose_cursor_time (location->start());
2736 show_verbose_cursor_time (location->end());
2739 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2742 case Selection::Toggle:
2743 /* we toggle on the button release */
2745 case Selection::Set:
2746 if (!_editor->selection->selected (_marker)) {
2747 _editor->selection->set (_marker);
2750 case Selection::Extend:
2752 Locations::LocationList ll;
2753 list<Marker*> to_add;
2755 _editor->selection->markers.range (s, e);
2756 s = min (_marker->position(), s);
2757 e = max (_marker->position(), e);
2760 if (e < max_framepos) {
2763 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2764 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2765 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2768 to_add.push_back (lm->start);
2771 to_add.push_back (lm->end);
2775 if (!to_add.empty()) {
2776 _editor->selection->add (to_add);
2780 case Selection::Add:
2781 _editor->selection->add (_marker);
2785 /* Set up copies for us to manipulate during the drag
2788 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2790 Location* l = _editor->find_location_from_marker (*i, is_start);
2797 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2799 /* range: check that the other end of the range isn't
2802 CopiedLocationInfo::iterator x;
2803 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2804 if (*(*x).location == *l) {
2808 if (x == _copied_locations.end()) {
2809 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2811 (*x).markers.push_back (*i);
2812 (*x).move_both = true;
2820 MarkerDrag::setup_pointer_frame_offset ()
2823 Location *location = _editor->find_location_from_marker (_marker, is_start);
2824 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2828 MarkerDrag::motion (GdkEvent* event, bool)
2830 framecnt_t f_delta = 0;
2832 bool move_both = false;
2833 Location *real_location;
2834 Location *copy_location = 0;
2836 framepos_t const newframe = adjusted_current_frame (event);
2837 framepos_t next = newframe;
2839 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2843 CopiedLocationInfo::iterator x;
2845 /* find the marker we're dragging, and compute the delta */
2847 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2849 copy_location = (*x).location;
2851 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2853 /* this marker is represented by this
2854 * CopiedLocationMarkerInfo
2857 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
2862 if (real_location->is_mark()) {
2863 f_delta = newframe - copy_location->start();
2867 switch (_marker->type()) {
2868 case Marker::SessionStart:
2869 case Marker::RangeStart:
2870 case Marker::LoopStart:
2871 case Marker::PunchIn:
2872 f_delta = newframe - copy_location->start();
2875 case Marker::SessionEnd:
2876 case Marker::RangeEnd:
2877 case Marker::LoopEnd:
2878 case Marker::PunchOut:
2879 f_delta = newframe - copy_location->end();
2882 /* what kind of marker is this ? */
2891 if (x == _copied_locations.end()) {
2892 /* hmm, impossible - we didn't find the dragged marker */
2896 /* now move them all */
2898 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2900 copy_location = x->location;
2902 /* call this to find out if its the start or end */
2904 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
2908 if (real_location->locked()) {
2912 if (copy_location->is_mark()) {
2916 copy_location->set_start (copy_location->start() + f_delta);
2920 framepos_t new_start = copy_location->start() + f_delta;
2921 framepos_t new_end = copy_location->end() + f_delta;
2923 if (is_start) { // start-of-range marker
2925 if (move_both || (*x).move_both) {
2926 copy_location->set_start (new_start);
2927 copy_location->set_end (new_end);
2928 } else if (new_start < copy_location->end()) {
2929 copy_location->set_start (new_start);
2930 } else if (newframe > 0) {
2931 _editor->snap_to (next, 1, true);
2932 copy_location->set_end (next);
2933 copy_location->set_start (newframe);
2936 } else { // end marker
2938 if (move_both || (*x).move_both) {
2939 copy_location->set_end (new_end);
2940 copy_location->set_start (new_start);
2941 } else if (new_end > copy_location->start()) {
2942 copy_location->set_end (new_end);
2943 } else if (newframe > 0) {
2944 _editor->snap_to (next, -1, true);
2945 copy_location->set_start (next);
2946 copy_location->set_end (newframe);
2951 update_item (copy_location);
2953 /* now lookup the actual GUI items used to display this
2954 * location and move them to wherever the copy of the location
2955 * is now. This means that the logic in ARDOUR::Location is
2956 * still enforced, even though we are not (yet) modifying
2957 * the real Location itself.
2960 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2963 lm->set_position (copy_location->start(), copy_location->end());
2968 assert (!_copied_locations.empty());
2970 show_verbose_cursor_time (newframe);
2974 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2976 if (!movement_occurred) {
2978 /* just a click, do nothing but finish
2979 off the selection process
2982 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2985 case Selection::Set:
2986 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2987 _editor->selection->set (_marker);
2991 case Selection::Toggle:
2992 /* we toggle on the button release, click only */
2993 _editor->selection->toggle (_marker);
2996 case Selection::Extend:
2997 case Selection::Add:
3004 _editor->_dragging_edit_point = false;
3006 _editor->begin_reversible_command ( _("move marker") );
3007 XMLNode &before = _editor->session()->locations()->get_state();
3009 MarkerSelection::iterator i;
3010 CopiedLocationInfo::iterator x;
3013 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3014 x != _copied_locations.end() && i != _editor->selection->markers.end();
3017 Location * location = _editor->find_location_from_marker (*i, is_start);
3021 if (location->locked()) {
3025 if (location->is_mark()) {
3026 location->set_start (((*x).location)->start());
3028 location->set (((*x).location)->start(), ((*x).location)->end());
3033 XMLNode &after = _editor->session()->locations()->get_state();
3034 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3035 _editor->commit_reversible_command ();
3039 MarkerDrag::aborted (bool)
3045 MarkerDrag::update_item (Location*)
3050 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3052 _cumulative_x_drag (0),
3053 _cumulative_y_drag (0)
3055 if (_zero_gain_fraction < 0.0) {
3056 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3059 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3061 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3067 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3069 Drag::start_grab (event, _editor->cursors()->fader);
3071 // start the grab at the center of the control point so
3072 // the point doesn't 'jump' to the mouse after the first drag
3073 _fixed_grab_x = _point->get_x();
3074 _fixed_grab_y = _point->get_y();
3076 float const fraction = 1 - (_point->get_y() / _point->line().height());
3078 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3080 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3081 event->button.x + 10, event->button.y + 10);
3083 _editor->verbose_cursor()->show ();
3085 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3087 if (!_point->can_slide ()) {
3088 _x_constrained = true;
3093 ControlPointDrag::motion (GdkEvent* event, bool)
3095 double dx = _drags->current_pointer_x() - last_pointer_x();
3096 double dy = _drags->current_pointer_y() - last_pointer_y();
3098 if (event->button.state & Keyboard::SecondaryModifier) {
3103 /* coordinate in pixels relative to the start of the region (for region-based automation)
3104 or track (for track-based automation) */
3105 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3106 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3108 // calculate zero crossing point. back off by .01 to stay on the
3109 // positive side of zero
3110 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3112 // make sure we hit zero when passing through
3113 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3117 if (_x_constrained) {
3120 if (_y_constrained) {
3124 _cumulative_x_drag = cx - _fixed_grab_x;
3125 _cumulative_y_drag = cy - _fixed_grab_y;
3129 cy = min ((double) _point->line().height(), cy);
3131 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3133 if (!_x_constrained) {
3134 _editor->snap_to_with_modifier (cx_frames, event);
3137 cx_frames = min (cx_frames, _point->line().maximum_time());
3139 float const fraction = 1.0 - (cy / _point->line().height());
3141 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3143 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3147 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3149 if (!movement_occurred) {
3153 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3154 _editor->reset_point_selection ();
3158 motion (event, false);
3161 _point->line().end_drag (_pushing, _final_index);
3162 _editor->session()->commit_reversible_command ();
3166 ControlPointDrag::aborted (bool)
3168 _point->line().reset ();
3172 ControlPointDrag::active (Editing::MouseMode m)
3174 if (m == Editing::MouseGain) {
3175 /* always active in mouse gain */
3179 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3180 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3183 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3186 _cumulative_y_drag (0)
3188 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3192 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3194 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3197 _item = &_line->grab_item ();
3199 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3200 origin, and ditto for y.
3203 double cx = event->button.x;
3204 double cy = event->button.y;
3206 _line->parent_group().canvas_to_item (cx, cy);
3208 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3213 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3214 /* no adjacent points */
3218 Drag::start_grab (event, _editor->cursors()->fader);
3220 /* store grab start in parent frame */
3225 double fraction = 1.0 - (cy / _line->height());
3227 _line->start_drag_line (before, after, fraction);
3229 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3230 event->button.x + 10, event->button.y + 10);
3232 _editor->verbose_cursor()->show ();
3236 LineDrag::motion (GdkEvent* event, bool)
3238 double dy = _drags->current_pointer_y() - last_pointer_y();
3240 if (event->button.state & Keyboard::SecondaryModifier) {
3244 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3246 _cumulative_y_drag = cy - _fixed_grab_y;
3249 cy = min ((double) _line->height(), cy);
3251 double const fraction = 1.0 - (cy / _line->height());
3254 /* we are ignoring x position for this drag, so we can just pass in anything */
3255 _line->drag_motion (0, fraction, true, false, ignored);
3257 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3261 LineDrag::finished (GdkEvent* event, bool)
3263 motion (event, false);
3264 _line->end_drag (false, 0);
3265 _editor->session()->commit_reversible_command ();
3269 LineDrag::aborted (bool)
3274 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3277 _cumulative_x_drag (0)
3279 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3283 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3285 Drag::start_grab (event);
3287 _line = reinterpret_cast<Line*> (_item);
3290 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3292 double cx = event->button.x;
3293 double cy = event->button.y;
3295 _item->parent()->canvas_to_item (cx, cy);
3297 /* store grab start in parent frame */
3298 _region_view_grab_x = cx;
3300 _before = *(float*) _item->get_data ("position");
3302 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3304 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3308 FeatureLineDrag::motion (GdkEvent*, bool)
3310 double dx = _drags->current_pointer_x() - last_pointer_x();
3312 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3314 _cumulative_x_drag += dx;
3316 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3325 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3327 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3329 float *pos = new float;
3332 _line->set_data ("position", pos);
3338 FeatureLineDrag::finished (GdkEvent*, bool)
3340 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3341 _arv->update_transient(_before, _before);
3345 FeatureLineDrag::aborted (bool)
3350 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3352 , _vertical_only (false)
3354 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3358 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3360 Drag::start_grab (event);
3361 show_verbose_cursor_time (adjusted_current_frame (event));
3365 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3372 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3374 framepos_t grab = grab_frame ();
3375 if (Config->get_rubberbanding_snaps_to_grid ()) {
3376 _editor->snap_to_with_modifier (grab, event);
3379 /* base start and end on initial click position */
3389 if (_drags->current_pointer_y() < grab_y()) {
3390 y1 = _drags->current_pointer_y();
3393 y2 = _drags->current_pointer_y();
3398 if (start != end || y1 != y2) {
3400 double x1 = _editor->sample_to_pixel (start);
3401 double x2 = _editor->sample_to_pixel (end);
3403 _editor->rubberband_rect->set_x0 (x1);
3404 if (_vertical_only) {
3405 /* fixed 10 pixel width */
3406 _editor->rubberband_rect->set_x1 (x1 + 10);
3408 _editor->rubberband_rect->set_x1 (x2);
3411 _editor->rubberband_rect->set_y0 (y1);
3412 _editor->rubberband_rect->set_y1 (y2);
3414 _editor->rubberband_rect->show();
3415 _editor->rubberband_rect->raise_to_top();
3417 show_verbose_cursor_time (pf);
3419 do_select_things (event, true);
3424 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3429 if (grab_frame() < last_pointer_frame()) {
3431 x2 = last_pointer_frame ();
3434 x1 = last_pointer_frame ();
3440 if (_drags->current_pointer_y() < grab_y()) {
3441 y1 = _drags->current_pointer_y();
3444 y2 = _drags->current_pointer_y();
3448 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3452 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3454 if (movement_occurred) {
3456 motion (event, false);
3457 do_select_things (event, false);
3463 bool do_deselect = true;
3464 MidiTimeAxisView* mtv;
3466 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3468 if (_editor->selection->empty()) {
3469 /* nothing selected */
3470 add_midi_region (mtv);
3471 do_deselect = false;
3475 /* do not deselect if Primary or Tertiary (toggle-select or
3476 * extend-select are pressed.
3479 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3480 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3487 _editor->rubberband_rect->hide();
3491 RubberbandSelectDrag::aborted (bool)
3493 _editor->rubberband_rect->hide ();
3496 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3497 : RegionDrag (e, i, p, v)
3499 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3503 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3505 Drag::start_grab (event, cursor);
3507 show_verbose_cursor_time (adjusted_current_frame (event));
3511 TimeFXDrag::motion (GdkEvent* event, bool)
3513 RegionView* rv = _primary;
3514 StreamView* cv = rv->get_time_axis_view().view ();
3516 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3517 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3518 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3520 framepos_t const pf = adjusted_current_frame (event);
3522 if (pf > rv->region()->position()) {
3523 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3526 show_verbose_cursor_time (pf);
3530 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3532 _primary->get_time_axis_view().hide_timestretch ();
3534 if (!movement_occurred) {
3538 if (last_pointer_frame() < _primary->region()->position()) {
3539 /* backwards drag of the left edge - not usable */
3543 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3545 float percentage = (double) newlen / (double) _primary->region()->length();
3547 #ifndef USE_RUBBERBAND
3548 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3549 if (_primary->region()->data_type() == DataType::AUDIO) {
3550 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3554 if (!_editor->get_selection().regions.empty()) {
3555 /* primary will already be included in the selection, and edit
3556 group shared editing will propagate selection across
3557 equivalent regions, so just use the current region
3561 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3562 error << _("An error occurred while executing time stretch operation") << endmsg;
3568 TimeFXDrag::aborted (bool)
3570 _primary->get_time_axis_view().hide_timestretch ();
3573 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3576 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3580 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3582 Drag::start_grab (event);
3586 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3588 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3592 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3594 if (movement_occurred && _editor->session()) {
3595 /* make sure we stop */
3596 _editor->session()->request_transport_speed (0.0);
3601 ScrubDrag::aborted (bool)
3606 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3611 , _original_pointer_time_axis (-1)
3612 , _last_pointer_time_axis (-1)
3613 , _time_selection_at_start (!_editor->get_selection().time.empty())
3615 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3617 if (_time_selection_at_start) {
3618 start_at_start = _editor->get_selection().time.start();
3619 end_at_start = _editor->get_selection().time.end_frame();
3624 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3626 if (_editor->session() == 0) {
3630 Gdk::Cursor* cursor = 0;
3632 switch (_operation) {
3633 case CreateSelection:
3634 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3639 cursor = _editor->cursors()->selector;
3640 Drag::start_grab (event, cursor);
3643 case SelectionStartTrim:
3644 if (_editor->clicked_axisview) {
3645 _editor->clicked_axisview->order_selection_trims (_item, true);
3647 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3650 case SelectionEndTrim:
3651 if (_editor->clicked_axisview) {
3652 _editor->clicked_axisview->order_selection_trims (_item, false);
3654 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3658 Drag::start_grab (event, cursor);
3661 case SelectionExtend:
3662 Drag::start_grab (event, cursor);
3666 if (_operation == SelectionMove) {
3667 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3669 show_verbose_cursor_time (adjusted_current_frame (event));
3672 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3676 SelectionDrag::setup_pointer_frame_offset ()
3678 switch (_operation) {
3679 case CreateSelection:
3680 _pointer_frame_offset = 0;
3683 case SelectionStartTrim:
3685 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3688 case SelectionEndTrim:
3689 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3692 case SelectionExtend:
3698 SelectionDrag::motion (GdkEvent* event, bool first_move)
3700 framepos_t start = 0;
3702 framecnt_t length = 0;
3703 framecnt_t distance = 0;
3705 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3706 if (pending_time_axis.first == 0) {
3710 framepos_t const pending_position = adjusted_current_frame (event);
3712 /* only alter selection if things have changed */
3714 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3718 switch (_operation) {
3719 case CreateSelection:
3721 framepos_t grab = grab_frame ();
3724 grab = adjusted_current_frame (event, false);
3725 if (grab < pending_position) {
3726 _editor->snap_to (grab, -1);
3728 _editor->snap_to (grab, 1);
3732 if (pending_position < grab) {
3733 start = pending_position;
3736 end = pending_position;
3740 /* first drag: Either add to the selection
3741 or create a new selection
3747 /* adding to the selection */
3748 _editor->set_selected_track_as_side_effect (Selection::Add);
3749 //_editor->selection->add (_editor->clicked_axisview);
3750 _editor->clicked_selection = _editor->selection->add (start, end);
3755 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3756 //_editor->selection->set (_editor->clicked_axisview);
3757 _editor->set_selected_track_as_side_effect (Selection::Set);
3760 _editor->clicked_selection = _editor->selection->set (start, end);
3764 /* select the track that we're in */
3765 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3766 // _editor->set_selected_track_as_side_effect (Selection::Add);
3767 _editor->selection->add (pending_time_axis.first);
3768 _added_time_axes.push_back (pending_time_axis.first);
3771 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3772 tracks that we selected in the first place.
3775 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3776 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3778 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3779 while (i != _added_time_axes.end()) {
3781 list<TimeAxisView*>::iterator tmp = i;
3784 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3785 _editor->selection->remove (*i);
3786 _added_time_axes.remove (*i);
3795 case SelectionStartTrim:
3797 start = _editor->selection->time[_editor->clicked_selection].start;
3798 end = _editor->selection->time[_editor->clicked_selection].end;
3800 if (pending_position > end) {
3803 start = pending_position;
3807 case SelectionEndTrim:
3809 start = _editor->selection->time[_editor->clicked_selection].start;
3810 end = _editor->selection->time[_editor->clicked_selection].end;
3812 if (pending_position < start) {
3815 end = pending_position;
3822 start = _editor->selection->time[_editor->clicked_selection].start;
3823 end = _editor->selection->time[_editor->clicked_selection].end;
3825 length = end - start;
3826 distance = pending_position - start;
3827 start = pending_position;
3828 _editor->snap_to (start);
3830 end = start + length;
3834 case SelectionExtend:
3838 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
3839 _editor->start_canvas_autoscroll (1, 0);
3843 switch (_operation) {
3845 if (_time_selection_at_start) {
3846 _editor->selection->move_time (distance);
3850 _editor->selection->replace (_editor->clicked_selection, start, end);
3854 if (_operation == SelectionMove) {
3855 show_verbose_cursor_time(start);
3857 show_verbose_cursor_time(pending_position);
3862 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3864 Session* s = _editor->session();
3866 if (movement_occurred) {
3867 motion (event, false);
3868 /* XXX this is not object-oriented programming at all. ick */
3869 if (_editor->selection->time.consolidate()) {
3870 _editor->selection->TimeChanged ();
3873 /* XXX what if its a music time selection? */
3875 if ( s->get_play_range() && s->transport_rolling() ) {
3876 s->request_play_range (&_editor->selection->time, true);
3878 if (Config->get_always_play_range() && !s->transport_rolling()) {
3879 s->request_locate (_editor->get_selection().time.start());
3885 /* just a click, no pointer movement.
3888 if (_operation == SelectionExtend) {
3889 if (_time_selection_at_start) {
3890 framepos_t pos = adjusted_current_frame (event, false);
3891 framepos_t start = min (pos, start_at_start);
3892 framepos_t end = max (pos, end_at_start);
3893 _editor->selection->set (start, end);
3896 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3897 if (_editor->clicked_selection) {
3898 _editor->selection->remove (_editor->clicked_selection);
3901 if (!_editor->clicked_selection) {
3902 _editor->selection->clear_time();
3907 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3908 _editor->selection->set (_editor->clicked_axisview);
3911 if (s && s->get_play_range () && s->transport_rolling()) {
3912 s->request_stop (false, false);
3917 _editor->stop_canvas_autoscroll ();
3918 _editor->clicked_selection = 0;
3922 SelectionDrag::aborted (bool)
3927 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3932 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3934 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
3935 ArdourCanvas::Rect (0.0, 0.0, 0.0,
3936 physical_screen_height (_editor->get_window())));
3937 _drag_rect->hide ();
3939 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3940 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3944 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3946 if (_editor->session() == 0) {
3950 Gdk::Cursor* cursor = 0;
3952 if (!_editor->temp_location) {
3953 _editor->temp_location = new Location (*_editor->session());
3956 switch (_operation) {
3957 case CreateRangeMarker:
3958 case CreateTransportMarker:
3959 case CreateCDMarker:
3961 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3966 cursor = _editor->cursors()->selector;
3970 Drag::start_grab (event, cursor);
3972 show_verbose_cursor_time (adjusted_current_frame (event));
3976 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3978 framepos_t start = 0;
3980 ArdourCanvas::Rectangle *crect;
3982 switch (_operation) {
3983 case CreateRangeMarker:
3984 crect = _editor->range_bar_drag_rect;
3986 case CreateTransportMarker:
3987 crect = _editor->transport_bar_drag_rect;
3989 case CreateCDMarker:
3990 crect = _editor->cd_marker_bar_drag_rect;
3993 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
3998 framepos_t const pf = adjusted_current_frame (event);
4000 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4001 framepos_t grab = grab_frame ();
4002 _editor->snap_to (grab);
4004 if (pf < grab_frame()) {
4012 /* first drag: Either add to the selection
4013 or create a new selection.
4018 _editor->temp_location->set (start, end);
4022 update_item (_editor->temp_location);
4024 //_drag_rect->raise_to_top();
4029 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
4030 _editor->start_canvas_autoscroll (1, 0);
4034 _editor->temp_location->set (start, end);
4036 double x1 = _editor->sample_to_pixel (start);
4037 double x2 = _editor->sample_to_pixel (end);
4041 update_item (_editor->temp_location);
4044 show_verbose_cursor_time (pf);
4049 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4051 Location * newloc = 0;
4055 if (movement_occurred) {
4056 motion (event, false);
4059 switch (_operation) {
4060 case CreateRangeMarker:
4061 case CreateCDMarker:
4063 _editor->begin_reversible_command (_("new range marker"));
4064 XMLNode &before = _editor->session()->locations()->get_state();
4065 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4066 if (_operation == CreateCDMarker) {
4067 flags = Location::IsRangeMarker | Location::IsCDMarker;
4068 _editor->cd_marker_bar_drag_rect->hide();
4071 flags = Location::IsRangeMarker;
4072 _editor->range_bar_drag_rect->hide();
4074 newloc = new Location (
4075 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4078 _editor->session()->locations()->add (newloc, true);
4079 XMLNode &after = _editor->session()->locations()->get_state();
4080 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4081 _editor->commit_reversible_command ();
4085 case CreateTransportMarker:
4086 // popup menu to pick loop or punch
4087 _editor->new_transport_marker_context_menu (&event->button, _item);
4091 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4093 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
4098 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4100 if (end == max_framepos) {
4101 end = _editor->session()->current_end_frame ();
4104 if (start == max_framepos) {
4105 start = _editor->session()->current_start_frame ();
4108 switch (_editor->mouse_mode) {
4110 /* find the two markers on either side and then make the selection from it */
4111 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4115 /* find the two markers on either side of the click and make the range out of it */
4116 _editor->selection->set (start, end);
4125 _editor->stop_canvas_autoscroll ();
4129 RangeMarkerBarDrag::aborted (bool)
4135 RangeMarkerBarDrag::update_item (Location* location)
4137 double const x1 = _editor->sample_to_pixel (location->start());
4138 double const x2 = _editor->sample_to_pixel (location->end());
4140 _drag_rect->set_x0 (x1);
4141 _drag_rect->set_x1 (x2);
4144 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4148 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4152 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4154 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4155 Drag::start_grab (event, _editor->cursors()->zoom_out);
4158 Drag::start_grab (event, _editor->cursors()->zoom_in);
4162 show_verbose_cursor_time (adjusted_current_frame (event));
4166 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4171 framepos_t const pf = adjusted_current_frame (event);
4173 framepos_t grab = grab_frame ();
4174 _editor->snap_to_with_modifier (grab, event);
4176 /* base start and end on initial click position */
4188 _editor->zoom_rect->show();
4189 _editor->zoom_rect->raise_to_top();
4192 _editor->reposition_zoom_rect(start, end);
4194 show_verbose_cursor_time (pf);
4199 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4201 if (movement_occurred) {
4202 motion (event, false);
4204 if (grab_frame() < last_pointer_frame()) {
4205 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4207 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4210 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4211 _editor->tav_zoom_step (_zoom_out);
4213 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4217 _editor->zoom_rect->hide();
4221 MouseZoomDrag::aborted (bool)
4223 _editor->zoom_rect->hide ();
4226 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4228 , _cumulative_dx (0)
4229 , _cumulative_dy (0)
4231 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4233 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4235 _region = &_primary->region_view ();
4236 _note_height = _region->midi_stream_view()->note_height ();
4240 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4242 Drag::start_grab (event);
4244 if (!(_was_selected = _primary->selected())) {
4246 /* tertiary-click means extend selection - we'll do that on button release,
4247 so don't add it here, because otherwise we make it hard to figure
4248 out the "extend-to" range.
4251 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4254 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4257 _region->note_selected (_primary, true);
4259 _region->unique_select (_primary);
4265 /** @return Current total drag x change in frames */
4267 NoteDrag::total_dx () const
4270 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4272 /* primary note time */
4273 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4275 /* new time of the primary note in session frames */
4276 frameoffset_t st = n + dx;
4278 framepos_t const rp = _region->region()->position ();
4280 /* prevent the note being dragged earlier than the region's position */
4283 /* snap and return corresponding delta */
4284 return _region->snap_frame_to_frame (st - rp) + rp - n;
4287 /** @return Current total drag y change in note number */
4289 NoteDrag::total_dy () const
4291 MidiStreamView* msv = _region->midi_stream_view ();
4292 double const y = _region->midi_view()->y_position ();
4293 /* new current note */
4294 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4296 n = max (msv->lowest_note(), n);
4297 n = min (msv->highest_note(), n);
4298 /* and work out delta */
4299 return n - msv->y_to_note (grab_y() - y);
4303 NoteDrag::motion (GdkEvent *, bool)
4305 /* Total change in x and y since the start of the drag */
4306 frameoffset_t const dx = total_dx ();
4307 int8_t const dy = total_dy ();
4309 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4310 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4311 double const tdy = -dy * _note_height - _cumulative_dy;
4314 _cumulative_dx += tdx;
4315 _cumulative_dy += tdy;
4317 int8_t note_delta = total_dy();
4319 _region->move_selection (tdx, tdy, note_delta);
4321 /* the new note value may be the same as the old one, but we
4322 * don't know what that means because the selection may have
4323 * involved more than one note and we might be doing something
4324 * odd with them. so show the note value anyway, always.
4328 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4330 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4331 (int) floor (new_note));
4333 show_verbose_cursor_text (buf);
4338 NoteDrag::finished (GdkEvent* ev, bool moved)
4341 /* no motion - select note */
4343 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4344 _editor->current_mouse_mode() == Editing::MouseDraw) {
4346 if (_was_selected) {
4347 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4349 _region->note_deselected (_primary);
4352 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4353 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4355 if (!extend && !add && _region->selection_size() > 1) {
4356 _region->unique_select (_primary);
4357 } else if (extend) {
4358 _region->note_selected (_primary, true, true);
4360 /* it was added during button press */
4365 _region->note_dropped (_primary, total_dx(), total_dy());
4370 NoteDrag::aborted (bool)
4375 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4376 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4377 : Drag (editor, atv->base_item ())
4379 , _nothing_to_drag (false)
4381 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4382 y_origin = atv->y_position();
4383 setup (atv->lines ());
4386 /** Make an AutomationRangeDrag for region gain lines */
4387 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4388 : Drag (editor, rv->get_canvas_group ())
4390 , _nothing_to_drag (false)
4392 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4394 list<boost::shared_ptr<AutomationLine> > lines;
4395 lines.push_back (rv->get_gain_line ());
4396 y_origin = rv->get_time_axis_view().y_position();
4400 /** @param lines AutomationLines to drag.
4401 * @param offset Offset from the session start to the points in the AutomationLines.
4404 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4406 /* find the lines that overlap the ranges being dragged */
4407 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4408 while (i != lines.end ()) {
4409 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4412 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4414 /* check this range against all the AudioRanges that we are using */
4415 list<AudioRange>::const_iterator k = _ranges.begin ();
4416 while (k != _ranges.end()) {
4417 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4423 /* add it to our list if it overlaps at all */
4424 if (k != _ranges.end()) {
4429 _lines.push_back (n);
4435 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4439 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4441 return 1.0 - ((global_y - y_origin) / line->height());
4445 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4447 Drag::start_grab (event, cursor);
4449 /* Get line states before we start changing things */
4450 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4451 i->state = &i->line->get_state ();
4452 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4455 if (_ranges.empty()) {
4457 /* No selected time ranges: drag all points */
4458 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4459 uint32_t const N = i->line->npoints ();
4460 for (uint32_t j = 0; j < N; ++j) {
4461 i->points.push_back (i->line->nth (j));
4467 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4469 framecnt_t const half = (i->start + i->end) / 2;
4471 /* find the line that this audio range starts in */
4472 list<Line>::iterator j = _lines.begin();
4473 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4477 if (j != _lines.end()) {
4478 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4480 /* j is the line that this audio range starts in; fade into it;
4481 64 samples length plucked out of thin air.
4484 framepos_t a = i->start + 64;
4489 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4490 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4492 the_list->add (p, the_list->eval (p));
4493 the_list->add (q, the_list->eval (q));
4496 /* same thing for the end */
4499 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4503 if (j != _lines.end()) {
4504 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4506 /* j is the line that this audio range starts in; fade out of it;
4507 64 samples length plucked out of thin air.
4510 framepos_t b = i->end - 64;
4515 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4516 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4518 the_list->add (p, the_list->eval (p));
4519 the_list->add (q, the_list->eval (q));
4523 _nothing_to_drag = true;
4525 /* Find all the points that should be dragged and put them in the relevant
4526 points lists in the Line structs.
4529 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4531 uint32_t const N = i->line->npoints ();
4532 for (uint32_t j = 0; j < N; ++j) {
4534 /* here's a control point on this line */
4535 ControlPoint* p = i->line->nth (j);
4536 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4538 /* see if it's inside a range */
4539 list<AudioRange>::const_iterator k = _ranges.begin ();
4540 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4544 if (k != _ranges.end()) {
4545 /* dragging this point */
4546 _nothing_to_drag = false;
4547 i->points.push_back (p);
4553 if (_nothing_to_drag) {
4557 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4558 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4563 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4565 if (_nothing_to_drag) {
4569 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4570 float const f = y_fraction (l->line, _drags->current_pointer_y());
4571 /* we are ignoring x position for this drag, so we can just pass in anything */
4573 l->line->drag_motion (0, f, true, false, ignored);
4574 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4579 AutomationRangeDrag::finished (GdkEvent* event, bool)
4581 if (_nothing_to_drag) {
4585 motion (event, false);
4586 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4587 i->line->end_drag (false, 0);
4590 _editor->session()->commit_reversible_command ();
4594 AutomationRangeDrag::aborted (bool)
4596 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4601 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4604 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4605 layer = v->region()->layer ();
4606 initial_y = v->get_canvas_group()->position().y;
4607 initial_playlist = v->region()->playlist ();
4608 initial_position = v->region()->position ();
4609 initial_end = v->region()->position () + v->region()->length ();
4612 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4613 : Drag (e, i->canvas_item ())
4616 , _cumulative_dx (0)
4618 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4619 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4624 PatchChangeDrag::motion (GdkEvent* ev, bool)
4626 framepos_t f = adjusted_current_frame (ev);
4627 boost::shared_ptr<Region> r = _region_view->region ();
4628 f = max (f, r->position ());
4629 f = min (f, r->last_frame ());
4631 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4632 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4633 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4634 _cumulative_dx = dxu;
4638 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4640 if (!movement_occurred) {
4644 boost::shared_ptr<Region> r (_region_view->region ());
4645 framepos_t f = adjusted_current_frame (ev);
4646 f = max (f, r->position ());
4647 f = min (f, r->last_frame ());
4649 _region_view->move_patch_change (
4651 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4656 PatchChangeDrag::aborted (bool)
4658 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4662 PatchChangeDrag::setup_pointer_frame_offset ()
4664 boost::shared_ptr<Region> region = _region_view->region ();
4665 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4668 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4669 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4676 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4678 framepos_t const p = _region_view->region()->position ();
4679 double const y = _region_view->midi_view()->y_position ();
4681 x1 = max ((framepos_t) 0, x1 - p);
4682 x2 = max ((framepos_t) 0, x2 - p);
4683 y1 = max (0.0, y1 - y);
4684 y2 = max (0.0, y2 - y);
4686 _region_view->update_drag_selection (
4687 _editor->sample_to_pixel (x1),
4688 _editor->sample_to_pixel (x2),
4691 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4696 MidiRubberbandSelectDrag::deselect_things ()
4701 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4702 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4705 _vertical_only = true;
4709 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4711 double const y = _region_view->midi_view()->y_position ();
4713 y1 = max (0.0, y1 - y);
4714 y2 = max (0.0, y2 - y);
4716 _region_view->update_vertical_drag_selection (
4719 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4724 MidiVerticalSelectDrag::deselect_things ()
4729 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4730 : RubberbandSelectDrag (e, i)
4736 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4738 if (drag_in_progress) {
4739 /* We just want to select things at the end of the drag, not during it */
4743 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4745 _editor->begin_reversible_command (_("rubberband selection"));
4746 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4747 _editor->commit_reversible_command ();
4751 EditorRubberbandSelectDrag::deselect_things ()
4753 if (!getenv("ARDOUR_SAE")) {
4754 _editor->selection->clear_tracks();
4756 _editor->selection->clear_regions();
4757 _editor->selection->clear_points ();
4758 _editor->selection->clear_lines ();
4761 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4769 NoteCreateDrag::~NoteCreateDrag ()
4775 NoteCreateDrag::grid_frames (framepos_t t) const
4778 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4783 return _region_view->region_beats_to_region_frames (grid_beats);
4787 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4789 Drag::start_grab (event, cursor);
4791 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4793 framepos_t pf = _drags->current_pointer_frame ();
4794 framecnt_t const g = grid_frames (pf);
4796 /* Hack so that we always snap to the note that we are over, instead of snapping
4797 to the next one if we're more than halfway through the one we're over.
4799 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4803 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4805 MidiStreamView* sv = _region_view->midi_stream_view ();
4806 double const x = _editor->sample_to_pixel (_note[0]);
4807 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4809 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4810 _drag_rect->set_outline_what (0xff);
4811 _drag_rect->set_outline_color (0xffffff99);
4812 _drag_rect->set_fill_color (0xffffff66);
4816 NoteCreateDrag::motion (GdkEvent* event, bool)
4818 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4819 double const x = _editor->sample_to_pixel (_note[1]);
4820 if (_note[1] > _note[0]) {
4821 _drag_rect->set_x1 (x);
4823 _drag_rect->set_x0 (x);
4828 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4830 if (!had_movement) {
4834 framepos_t const start = min (_note[0], _note[1]);
4835 framecnt_t length = (framecnt_t) fabs (_note[0] - _note[1]);
4837 framecnt_t const g = grid_frames (start);
4838 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4840 if (_editor->snap_mode() == SnapNormal && length < g) {
4841 length = g - one_tick;
4844 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4846 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
4850 NoteCreateDrag::y_to_region (double y) const
4853 _region_view->get_canvas_group()->canvas_to_item (x, y);
4858 NoteCreateDrag::aborted (bool)
4863 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4868 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
4872 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4874 Drag::start_grab (event, cursor);
4878 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4884 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4887 distance = _drags->current_pointer_x() - grab_x();
4888 len = ar->fade_in()->back()->when;
4890 distance = grab_x() - _drags->current_pointer_x();
4891 len = ar->fade_out()->back()->when;
4894 /* how long should it be ? */
4896 new_length = len + _editor->pixel_to_sample (distance);
4898 /* now check with the region that this is legal */
4900 new_length = ar->verify_xfade_bounds (new_length, start);
4903 arv->reset_fade_in_shape_width (ar, new_length);
4905 arv->reset_fade_out_shape_width (ar, new_length);
4910 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
4916 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4919 distance = _drags->current_pointer_x() - grab_x();
4920 len = ar->fade_in()->back()->when;
4922 distance = grab_x() - _drags->current_pointer_x();
4923 len = ar->fade_out()->back()->when;
4926 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
4928 _editor->begin_reversible_command ("xfade trim");
4929 ar->playlist()->clear_owned_changes ();
4932 ar->set_fade_in_length (new_length);
4934 ar->set_fade_out_length (new_length);
4937 /* Adjusting the xfade may affect other regions in the playlist, so we need
4938 to get undo Commands from the whole playlist rather than just the
4942 vector<Command*> cmds;
4943 ar->playlist()->rdiff (cmds);
4944 _editor->session()->add_commands (cmds);
4945 _editor->commit_reversible_command ();
4950 CrossfadeEdgeDrag::aborted (bool)
4953 arv->redraw_start_xfade ();
4955 arv->redraw_end_xfade ();