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;
1834 if (tv && tv->is_track()) {
1835 speed = tv->track()->speed();
1838 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1844 switch (_operation) {
1846 trim_type = "Region start trim";
1849 trim_type = "Region end trim";
1852 trim_type = "Region content trim";
1856 _editor->begin_reversible_command (trim_type);
1858 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1859 RegionView* rv = i->view;
1860 rv->fake_set_opaque (false);
1861 rv->enable_display (false);
1862 rv->region()->playlist()->clear_owned_changes ();
1864 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1867 arv->temporarily_hide_envelope ();
1871 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1872 insert_result = _editor->motion_frozen_playlists.insert (pl);
1874 if (insert_result.second) {
1880 bool non_overlap_trim = false;
1882 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1883 non_overlap_trim = true;
1886 switch (_operation) {
1888 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1889 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1890 if (changed && _preserve_fade_anchor) {
1891 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1896 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1897 distance = _drags->current_pointer_x() - grab_x();
1898 len = ar->fade_in()->back()->when;
1899 new_length = len - _editor->pixel_to_sample (distance);
1900 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
1901 arv->reset_fade_in_shape_width (ar, new_length); //the grey shape
1908 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1909 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1910 if (changed && _preserve_fade_anchor) {
1911 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1916 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1917 distance = grab_x() - _drags->current_pointer_x();
1918 len = ar->fade_out()->back()->when;
1919 new_length = len - _editor->pixel_to_sample (distance);
1920 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
1921 arv->reset_fade_out_shape_width (ar, new_length); //the grey shape
1929 bool swap_direction = false;
1931 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1932 swap_direction = true;
1935 framecnt_t frame_delta = 0;
1937 bool left_direction = false;
1938 if (last_pointer_frame() > adjusted_current_frame(event)) {
1939 left_direction = true;
1942 if (left_direction) {
1943 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1945 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1948 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1949 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1955 switch (_operation) {
1957 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1960 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1963 show_verbose_cursor_time (adjusted_current_frame (event));
1970 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1972 if (movement_occurred) {
1973 motion (event, false);
1975 if (_operation == StartTrim) {
1976 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1978 /* This must happen before the region's StatefulDiffCommand is created, as it may
1979 `correct' (ahem) the region's _start from being negative to being zero. It
1980 needs to be zero in the undo record.
1982 i->view->trim_front_ending ();
1984 if (_preserve_fade_anchor) {
1985 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1990 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1991 distance = _drags->current_pointer_x() - grab_x();
1992 len = ar->fade_in()->back()->when;
1993 new_length = len - _editor->pixel_to_sample (distance);
1994 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
1995 ar->set_fade_in_length(new_length);
1999 } else if (_operation == EndTrim) {
2000 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2001 if (_preserve_fade_anchor) {
2002 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2007 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2008 distance = _drags->current_pointer_x() - grab_x();
2009 len = ar->fade_out()->back()->when;
2010 new_length = len - _editor->pixel_to_sample (distance);
2011 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2012 ar->set_fade_out_length(new_length);
2018 if (!_views.empty()) {
2019 if (_operation == StartTrim) {
2020 _editor->maybe_locate_with_edit_preroll(
2021 _views.begin()->view->region()->position());
2023 if (_operation == EndTrim) {
2024 _editor->maybe_locate_with_edit_preroll(
2025 _views.begin()->view->region()->position() +
2026 _views.begin()->view->region()->length());
2030 if (!_editor->selection->selected (_primary)) {
2031 _primary->thaw_after_trim ();
2034 set<boost::shared_ptr<Playlist> > diffed_playlists;
2036 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2037 i->view->thaw_after_trim ();
2038 i->view->enable_display (true);
2039 i->view->fake_set_opaque (true);
2041 /* Trimming one region may affect others on the playlist, so we need
2042 to get undo Commands from the whole playlist rather than just the
2043 region. Use diffed_playlists to make sure we don't diff a given
2044 playlist more than once.
2046 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2047 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2048 vector<Command*> cmds;
2050 _editor->session()->add_commands (cmds);
2051 diffed_playlists.insert (p);
2056 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2060 _editor->motion_frozen_playlists.clear ();
2061 _editor->commit_reversible_command();
2064 /* no mouse movement */
2065 _editor->point_trim (event, adjusted_current_frame (event));
2068 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2069 if (_operation == StartTrim) {
2070 i->view->trim_front_ending ();
2073 i->view->region()->resume_property_changes ();
2078 TrimDrag::aborted (bool movement_occurred)
2080 /* Our motion method is changing model state, so use the Undo system
2081 to cancel. Perhaps not ideal, as this will leave an Undo point
2082 behind which may be slightly odd from the user's point of view.
2087 if (movement_occurred) {
2091 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2092 i->view->region()->resume_property_changes ();
2097 TrimDrag::setup_pointer_frame_offset ()
2099 list<DraggingView>::iterator i = _views.begin ();
2100 while (i != _views.end() && i->view != _primary) {
2104 if (i == _views.end()) {
2108 switch (_operation) {
2110 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2113 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2120 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2124 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2125 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2130 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2132 Drag::start_grab (event, cursor);
2133 show_verbose_cursor_time (adjusted_current_frame(event));
2137 MeterMarkerDrag::setup_pointer_frame_offset ()
2139 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2143 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2147 // create a dummy marker for visual representation of moving the
2148 // section, because whether its a copy or not, we're going to
2149 // leave or lose the original marker (leave if its a copy; lose if its
2150 // not, because we'll remove it from the map).
2152 MeterSection section (_marker->meter());
2154 if (!section.movable()) {
2159 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2161 _marker = new MeterMarker (
2163 *_editor->meter_group,
2164 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2166 *new MeterSection (_marker->meter())
2169 /* use the new marker for the grab */
2170 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2173 TempoMap& map (_editor->session()->tempo_map());
2174 /* get current state */
2175 before_state = &map.get_state();
2176 /* remove the section while we drag it */
2177 map.remove_meter (section, true);
2181 framepos_t const pf = adjusted_current_frame (event);
2182 _marker->set_position (pf);
2183 show_verbose_cursor_time (pf);
2187 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2189 if (!movement_occurred) {
2193 motion (event, false);
2195 Timecode::BBT_Time when;
2197 TempoMap& map (_editor->session()->tempo_map());
2198 map.bbt_time (last_pointer_frame(), when);
2200 if (_copy == true) {
2201 _editor->begin_reversible_command (_("copy meter mark"));
2202 XMLNode &before = map.get_state();
2203 map.add_meter (_marker->meter(), when);
2204 XMLNode &after = map.get_state();
2205 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2206 _editor->commit_reversible_command ();
2209 _editor->begin_reversible_command (_("move meter mark"));
2211 /* we removed it before, so add it back now */
2213 map.add_meter (_marker->meter(), when);
2214 XMLNode &after = map.get_state();
2215 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2216 _editor->commit_reversible_command ();
2219 // delete the dummy marker we used for visual representation while moving.
2220 // a new visual marker will show up automatically.
2225 MeterMarkerDrag::aborted (bool moved)
2227 _marker->set_position (_marker->meter().frame ());
2230 TempoMap& map (_editor->session()->tempo_map());
2231 /* we removed it before, so add it back now */
2232 map.add_meter (_marker->meter(), _marker->meter().frame());
2233 // delete the dummy marker we used for visual representation while moving.
2234 // a new visual marker will show up automatically.
2239 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2243 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2245 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2250 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2252 Drag::start_grab (event, cursor);
2253 show_verbose_cursor_time (adjusted_current_frame (event));
2257 TempoMarkerDrag::setup_pointer_frame_offset ()
2259 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2263 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2267 // create a dummy marker for visual representation of moving the
2268 // section, because whether its a copy or not, we're going to
2269 // leave or lose the original marker (leave if its a copy; lose if its
2270 // not, because we'll remove it from the map).
2272 // create a dummy marker for visual representation of moving the copy.
2273 // The actual copying is not done before we reach the finish callback.
2276 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2278 TempoSection section (_marker->tempo());
2280 _marker = new TempoMarker (
2282 *_editor->tempo_group,
2283 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2285 *new TempoSection (_marker->tempo())
2288 /* use the new marker for the grab */
2289 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2292 TempoMap& map (_editor->session()->tempo_map());
2293 /* get current state */
2294 before_state = &map.get_state();
2295 /* remove the section while we drag it */
2296 map.remove_tempo (section, true);
2300 framepos_t const pf = adjusted_current_frame (event);
2301 _marker->set_position (pf);
2302 show_verbose_cursor_time (pf);
2306 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2308 if (!movement_occurred) {
2312 motion (event, false);
2314 TempoMap& map (_editor->session()->tempo_map());
2315 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2316 Timecode::BBT_Time when;
2318 map.bbt_time (beat_time, when);
2320 if (_copy == true) {
2321 _editor->begin_reversible_command (_("copy tempo mark"));
2322 XMLNode &before = map.get_state();
2323 map.add_tempo (_marker->tempo(), when);
2324 XMLNode &after = map.get_state();
2325 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2326 _editor->commit_reversible_command ();
2329 _editor->begin_reversible_command (_("move tempo mark"));
2330 /* we removed it before, so add it back now */
2331 map.add_tempo (_marker->tempo(), when);
2332 XMLNode &after = map.get_state();
2333 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2334 _editor->commit_reversible_command ();
2337 // delete the dummy marker we used for visual representation while moving.
2338 // a new visual marker will show up automatically.
2343 TempoMarkerDrag::aborted (bool moved)
2345 _marker->set_position (_marker->tempo().frame());
2347 TempoMap& map (_editor->session()->tempo_map());
2348 /* we removed it before, so add it back now */
2349 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2350 // delete the dummy marker we used for visual representation while moving.
2351 // a new visual marker will show up automatically.
2356 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2357 : Drag (e, &c.time_bar_canvas_item())
2361 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2364 /** Do all the things we do when dragging the playhead to make it look as though
2365 * we have located, without actually doing the locate (because that would cause
2366 * the diskstream buffers to be refilled, which is too slow).
2369 CursorDrag::fake_locate (framepos_t t)
2371 _editor->playhead_cursor->set_position (t);
2373 Session* s = _editor->session ();
2374 if (s->timecode_transmission_suspended ()) {
2375 framepos_t const f = _editor->playhead_cursor->current_frame ();
2376 s->send_mmc_locate (f);
2377 s->send_full_time_code (f);
2380 show_verbose_cursor_time (t);
2381 _editor->UpdateAllTransportClocks (t);
2385 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2387 Drag::start_grab (event, c);
2389 _grab_zoom = _editor->samples_per_pixel;
2391 framepos_t where = _editor->canvas_event_frame (event, 0, 0);
2393 _editor->snap_to_with_modifier (where, event);
2395 _editor->_dragging_playhead = true;
2397 Session* s = _editor->session ();
2399 /* grab the track canvas item as well */
2401 _cursor.track_canvas_item().grab();
2404 if (_was_rolling && _stop) {
2408 if (s->is_auditioning()) {
2409 s->cancel_audition ();
2413 if (AudioEngine::instance()->connected()) {
2415 /* do this only if we're the engine is connected
2416 * because otherwise this request will never be
2417 * serviced and we'll busy wait forever. likewise,
2418 * notice if we are disconnected while waiting for the
2419 * request to be serviced.
2422 s->request_suspend_timecode_transmission ();
2423 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2424 /* twiddle our thumbs */
2429 fake_locate (where);
2433 CursorDrag::motion (GdkEvent* event, bool)
2435 framepos_t const adjusted_frame = adjusted_current_frame (event);
2436 if (adjusted_frame != last_pointer_frame()) {
2437 fake_locate (adjusted_frame);
2442 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2444 _editor->_dragging_playhead = false;
2446 _cursor.track_canvas_item().ungrab();
2448 if (!movement_occurred && _stop) {
2452 motion (event, false);
2454 Session* s = _editor->session ();
2456 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2457 _editor->_pending_locate_request = true;
2458 s->request_resume_timecode_transmission ();
2463 CursorDrag::aborted (bool)
2465 _cursor.track_canvas_item().ungrab();
2467 if (_editor->_dragging_playhead) {
2468 _editor->session()->request_resume_timecode_transmission ();
2469 _editor->_dragging_playhead = false;
2472 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2475 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2476 : RegionDrag (e, i, p, v)
2478 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2482 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2484 Drag::start_grab (event, cursor);
2486 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2487 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2489 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2493 FadeInDrag::setup_pointer_frame_offset ()
2495 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2496 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2497 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2501 FadeInDrag::motion (GdkEvent* event, bool)
2503 framecnt_t fade_length;
2505 framepos_t const pos = adjusted_current_frame (event);
2507 boost::shared_ptr<Region> region = _primary->region ();
2509 if (pos < (region->position() + 64)) {
2510 fade_length = 64; // this should be a minimum defined somewhere
2511 } else if (pos > region->last_frame()) {
2512 fade_length = region->length();
2514 fade_length = pos - region->position();
2517 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2519 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2525 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2528 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2532 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2534 if (!movement_occurred) {
2538 framecnt_t fade_length;
2540 framepos_t const pos = adjusted_current_frame (event);
2542 boost::shared_ptr<Region> region = _primary->region ();
2544 if (pos < (region->position() + 64)) {
2545 fade_length = 64; // this should be a minimum defined somewhere
2546 } else if (pos > region->last_frame()) {
2547 fade_length = region->length();
2549 fade_length = pos - region->position();
2552 _editor->begin_reversible_command (_("change fade in length"));
2554 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2556 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2562 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2563 XMLNode &before = alist->get_state();
2565 tmp->audio_region()->set_fade_in_length (fade_length);
2566 tmp->audio_region()->set_fade_in_active (true);
2568 XMLNode &after = alist->get_state();
2569 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2572 _editor->commit_reversible_command ();
2576 FadeInDrag::aborted (bool)
2578 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2579 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2585 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2589 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2590 : RegionDrag (e, i, p, v)
2592 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2596 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2598 Drag::start_grab (event, cursor);
2600 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2601 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2603 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2607 FadeOutDrag::setup_pointer_frame_offset ()
2609 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2610 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2611 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2615 FadeOutDrag::motion (GdkEvent* event, bool)
2617 framecnt_t fade_length;
2619 framepos_t const pos = adjusted_current_frame (event);
2621 boost::shared_ptr<Region> region = _primary->region ();
2623 if (pos > (region->last_frame() - 64)) {
2624 fade_length = 64; // this should really be a minimum fade defined somewhere
2626 else if (pos < region->position()) {
2627 fade_length = region->length();
2630 fade_length = region->last_frame() - pos;
2633 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2635 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2641 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2644 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2648 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2650 if (!movement_occurred) {
2654 framecnt_t fade_length;
2656 framepos_t const pos = adjusted_current_frame (event);
2658 boost::shared_ptr<Region> region = _primary->region ();
2660 if (pos > (region->last_frame() - 64)) {
2661 fade_length = 64; // this should really be a minimum fade defined somewhere
2663 else if (pos < region->position()) {
2664 fade_length = region->length();
2667 fade_length = region->last_frame() - pos;
2670 _editor->begin_reversible_command (_("change fade out length"));
2672 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2674 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2680 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2681 XMLNode &before = alist->get_state();
2683 tmp->audio_region()->set_fade_out_length (fade_length);
2684 tmp->audio_region()->set_fade_out_active (true);
2686 XMLNode &after = alist->get_state();
2687 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2690 _editor->commit_reversible_command ();
2694 FadeOutDrag::aborted (bool)
2696 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2697 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2703 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2707 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2710 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2712 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2715 _points.push_back (ArdourCanvas::Duple (0, 0));
2716 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2719 MarkerDrag::~MarkerDrag ()
2721 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2726 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2728 location = new Location (*l);
2729 markers.push_back (m);
2734 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2736 Drag::start_grab (event, cursor);
2740 Location *location = _editor->find_location_from_marker (_marker, is_start);
2741 _editor->_dragging_edit_point = true;
2743 update_item (location);
2745 // _drag_line->show();
2746 // _line->raise_to_top();
2749 show_verbose_cursor_time (location->start());
2751 show_verbose_cursor_time (location->end());
2754 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2757 case Selection::Toggle:
2758 /* we toggle on the button release */
2760 case Selection::Set:
2761 if (!_editor->selection->selected (_marker)) {
2762 _editor->selection->set (_marker);
2765 case Selection::Extend:
2767 Locations::LocationList ll;
2768 list<Marker*> to_add;
2770 _editor->selection->markers.range (s, e);
2771 s = min (_marker->position(), s);
2772 e = max (_marker->position(), e);
2775 if (e < max_framepos) {
2778 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2779 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2780 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2783 to_add.push_back (lm->start);
2786 to_add.push_back (lm->end);
2790 if (!to_add.empty()) {
2791 _editor->selection->add (to_add);
2795 case Selection::Add:
2796 _editor->selection->add (_marker);
2800 /* Set up copies for us to manipulate during the drag
2803 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2805 Location* l = _editor->find_location_from_marker (*i, is_start);
2812 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2814 /* range: check that the other end of the range isn't
2817 CopiedLocationInfo::iterator x;
2818 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2819 if (*(*x).location == *l) {
2823 if (x == _copied_locations.end()) {
2824 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2826 (*x).markers.push_back (*i);
2827 (*x).move_both = true;
2835 MarkerDrag::setup_pointer_frame_offset ()
2838 Location *location = _editor->find_location_from_marker (_marker, is_start);
2839 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2843 MarkerDrag::motion (GdkEvent* event, bool)
2845 framecnt_t f_delta = 0;
2847 bool move_both = false;
2848 Location *real_location;
2849 Location *copy_location = 0;
2851 framepos_t const newframe = adjusted_current_frame (event);
2852 framepos_t next = newframe;
2854 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2858 CopiedLocationInfo::iterator x;
2860 /* find the marker we're dragging, and compute the delta */
2862 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2864 copy_location = (*x).location;
2866 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2868 /* this marker is represented by this
2869 * CopiedLocationMarkerInfo
2872 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
2877 if (real_location->is_mark()) {
2878 f_delta = newframe - copy_location->start();
2882 switch (_marker->type()) {
2883 case Marker::SessionStart:
2884 case Marker::RangeStart:
2885 case Marker::LoopStart:
2886 case Marker::PunchIn:
2887 f_delta = newframe - copy_location->start();
2890 case Marker::SessionEnd:
2891 case Marker::RangeEnd:
2892 case Marker::LoopEnd:
2893 case Marker::PunchOut:
2894 f_delta = newframe - copy_location->end();
2897 /* what kind of marker is this ? */
2906 if (x == _copied_locations.end()) {
2907 /* hmm, impossible - we didn't find the dragged marker */
2911 /* now move them all */
2913 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2915 copy_location = x->location;
2917 /* call this to find out if its the start or end */
2919 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
2923 if (real_location->locked()) {
2927 if (copy_location->is_mark()) {
2931 copy_location->set_start (copy_location->start() + f_delta);
2935 framepos_t new_start = copy_location->start() + f_delta;
2936 framepos_t new_end = copy_location->end() + f_delta;
2938 if (is_start) { // start-of-range marker
2940 if (move_both || (*x).move_both) {
2941 copy_location->set_start (new_start);
2942 copy_location->set_end (new_end);
2943 } else if (new_start < copy_location->end()) {
2944 copy_location->set_start (new_start);
2945 } else if (newframe > 0) {
2946 _editor->snap_to (next, 1, true);
2947 copy_location->set_end (next);
2948 copy_location->set_start (newframe);
2951 } else { // end marker
2953 if (move_both || (*x).move_both) {
2954 copy_location->set_end (new_end);
2955 copy_location->set_start (new_start);
2956 } else if (new_end > copy_location->start()) {
2957 copy_location->set_end (new_end);
2958 } else if (newframe > 0) {
2959 _editor->snap_to (next, -1, true);
2960 copy_location->set_start (next);
2961 copy_location->set_end (newframe);
2966 update_item (copy_location);
2968 /* now lookup the actual GUI items used to display this
2969 * location and move them to wherever the copy of the location
2970 * is now. This means that the logic in ARDOUR::Location is
2971 * still enforced, even though we are not (yet) modifying
2972 * the real Location itself.
2975 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2978 lm->set_position (copy_location->start(), copy_location->end());
2983 assert (!_copied_locations.empty());
2985 show_verbose_cursor_time (newframe);
2989 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2991 if (!movement_occurred) {
2993 /* just a click, do nothing but finish
2994 off the selection process
2997 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3000 case Selection::Set:
3001 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3002 _editor->selection->set (_marker);
3006 case Selection::Toggle:
3007 /* we toggle on the button release, click only */
3008 _editor->selection->toggle (_marker);
3011 case Selection::Extend:
3012 case Selection::Add:
3019 _editor->_dragging_edit_point = false;
3021 _editor->begin_reversible_command ( _("move marker") );
3022 XMLNode &before = _editor->session()->locations()->get_state();
3024 MarkerSelection::iterator i;
3025 CopiedLocationInfo::iterator x;
3028 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3029 x != _copied_locations.end() && i != _editor->selection->markers.end();
3032 Location * location = _editor->find_location_from_marker (*i, is_start);
3036 if (location->locked()) {
3040 if (location->is_mark()) {
3041 location->set_start (((*x).location)->start());
3043 location->set (((*x).location)->start(), ((*x).location)->end());
3048 XMLNode &after = _editor->session()->locations()->get_state();
3049 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3050 _editor->commit_reversible_command ();
3054 MarkerDrag::aborted (bool)
3060 MarkerDrag::update_item (Location*)
3065 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3067 _cumulative_x_drag (0),
3068 _cumulative_y_drag (0)
3070 if (_zero_gain_fraction < 0.0) {
3071 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3074 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3076 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3082 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3084 Drag::start_grab (event, _editor->cursors()->fader);
3086 // start the grab at the center of the control point so
3087 // the point doesn't 'jump' to the mouse after the first drag
3088 _fixed_grab_x = _point->get_x();
3089 _fixed_grab_y = _point->get_y();
3091 float const fraction = 1 - (_point->get_y() / _point->line().height());
3093 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3095 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3096 event->button.x + 10, event->button.y + 10);
3098 _editor->verbose_cursor()->show ();
3100 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3102 if (!_point->can_slide ()) {
3103 _x_constrained = true;
3108 ControlPointDrag::motion (GdkEvent* event, bool)
3110 double dx = _drags->current_pointer_x() - last_pointer_x();
3111 double dy = _drags->current_pointer_y() - last_pointer_y();
3113 if (event->button.state & Keyboard::SecondaryModifier) {
3118 /* coordinate in pixels relative to the start of the region (for region-based automation)
3119 or track (for track-based automation) */
3120 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3121 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3123 // calculate zero crossing point. back off by .01 to stay on the
3124 // positive side of zero
3125 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3127 // make sure we hit zero when passing through
3128 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3132 if (_x_constrained) {
3135 if (_y_constrained) {
3139 _cumulative_x_drag = cx - _fixed_grab_x;
3140 _cumulative_y_drag = cy - _fixed_grab_y;
3144 cy = min ((double) _point->line().height(), cy);
3146 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3148 if (!_x_constrained) {
3149 _editor->snap_to_with_modifier (cx_frames, event);
3152 cx_frames = min (cx_frames, _point->line().maximum_time());
3154 float const fraction = 1.0 - (cy / _point->line().height());
3156 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3158 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3162 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3164 if (!movement_occurred) {
3168 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3169 _editor->reset_point_selection ();
3173 motion (event, false);
3176 _point->line().end_drag (_pushing, _final_index);
3177 _editor->session()->commit_reversible_command ();
3181 ControlPointDrag::aborted (bool)
3183 _point->line().reset ();
3187 ControlPointDrag::active (Editing::MouseMode m)
3189 if (m == Editing::MouseGain) {
3190 /* always active in mouse gain */
3194 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3195 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3198 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3201 _cumulative_y_drag (0)
3203 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3207 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3209 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3212 _item = &_line->grab_item ();
3214 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3215 origin, and ditto for y.
3218 double cx = event->button.x;
3219 double cy = event->button.y;
3221 _line->parent_group().canvas_to_item (cx, cy);
3223 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3228 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3229 /* no adjacent points */
3233 Drag::start_grab (event, _editor->cursors()->fader);
3235 /* store grab start in parent frame */
3240 double fraction = 1.0 - (cy / _line->height());
3242 _line->start_drag_line (before, after, fraction);
3244 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3245 event->button.x + 10, event->button.y + 10);
3247 _editor->verbose_cursor()->show ();
3251 LineDrag::motion (GdkEvent* event, bool)
3253 double dy = _drags->current_pointer_y() - last_pointer_y();
3255 if (event->button.state & Keyboard::SecondaryModifier) {
3259 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3261 _cumulative_y_drag = cy - _fixed_grab_y;
3264 cy = min ((double) _line->height(), cy);
3266 double const fraction = 1.0 - (cy / _line->height());
3269 /* we are ignoring x position for this drag, so we can just pass in anything */
3270 _line->drag_motion (0, fraction, true, false, ignored);
3272 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3276 LineDrag::finished (GdkEvent* event, bool)
3278 motion (event, false);
3279 _line->end_drag (false, 0);
3280 _editor->session()->commit_reversible_command ();
3284 LineDrag::aborted (bool)
3289 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3292 _cumulative_x_drag (0)
3294 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3298 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3300 Drag::start_grab (event);
3302 _line = reinterpret_cast<Line*> (_item);
3305 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3307 double cx = event->button.x;
3308 double cy = event->button.y;
3310 _item->parent()->canvas_to_item (cx, cy);
3312 /* store grab start in parent frame */
3313 _region_view_grab_x = cx;
3315 _before = *(float*) _item->get_data ("position");
3317 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3319 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3323 FeatureLineDrag::motion (GdkEvent*, bool)
3325 double dx = _drags->current_pointer_x() - last_pointer_x();
3327 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3329 _cumulative_x_drag += dx;
3331 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3340 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3342 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3344 float *pos = new float;
3347 _line->set_data ("position", pos);
3353 FeatureLineDrag::finished (GdkEvent*, bool)
3355 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3356 _arv->update_transient(_before, _before);
3360 FeatureLineDrag::aborted (bool)
3365 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3367 , _vertical_only (false)
3369 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3373 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3375 Drag::start_grab (event);
3376 show_verbose_cursor_time (adjusted_current_frame (event));
3380 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3387 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3389 framepos_t grab = grab_frame ();
3390 if (Config->get_rubberbanding_snaps_to_grid ()) {
3391 _editor->snap_to_with_modifier (grab, event);
3394 /* base start and end on initial click position */
3404 if (_drags->current_pointer_y() < grab_y()) {
3405 y1 = _drags->current_pointer_y();
3408 y2 = _drags->current_pointer_y();
3413 if (start != end || y1 != y2) {
3415 double x1 = _editor->sample_to_pixel (start);
3416 double x2 = _editor->sample_to_pixel (end);
3418 _editor->rubberband_rect->set_x0 (x1);
3419 if (_vertical_only) {
3420 /* fixed 10 pixel width */
3421 _editor->rubberband_rect->set_x1 (x1 + 10);
3423 _editor->rubberband_rect->set_x1 (x2);
3426 _editor->rubberband_rect->set_y0 (y1);
3427 _editor->rubberband_rect->set_y1 (y2);
3429 _editor->rubberband_rect->show();
3430 _editor->rubberband_rect->raise_to_top();
3432 show_verbose_cursor_time (pf);
3434 do_select_things (event, true);
3439 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3444 if (grab_frame() < last_pointer_frame()) {
3446 x2 = last_pointer_frame ();
3449 x1 = last_pointer_frame ();
3455 if (_drags->current_pointer_y() < grab_y()) {
3456 y1 = _drags->current_pointer_y();
3459 y2 = _drags->current_pointer_y();
3463 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3467 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3469 if (movement_occurred) {
3471 motion (event, false);
3472 do_select_things (event, false);
3478 bool do_deselect = true;
3479 MidiTimeAxisView* mtv;
3481 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3483 if (_editor->selection->empty()) {
3484 /* nothing selected */
3485 add_midi_region (mtv);
3486 do_deselect = false;
3490 /* do not deselect if Primary or Tertiary (toggle-select or
3491 * extend-select are pressed.
3494 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3495 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3502 _editor->rubberband_rect->hide();
3506 RubberbandSelectDrag::aborted (bool)
3508 _editor->rubberband_rect->hide ();
3511 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3512 : RegionDrag (e, i, p, v)
3514 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3518 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3520 Drag::start_grab (event, cursor);
3522 show_verbose_cursor_time (adjusted_current_frame (event));
3526 TimeFXDrag::motion (GdkEvent* event, bool)
3528 RegionView* rv = _primary;
3529 StreamView* cv = rv->get_time_axis_view().view ();
3531 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3532 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3533 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3535 framepos_t const pf = adjusted_current_frame (event);
3537 if (pf > rv->region()->position()) {
3538 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3541 show_verbose_cursor_time (pf);
3545 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3547 _primary->get_time_axis_view().hide_timestretch ();
3549 if (!movement_occurred) {
3553 if (last_pointer_frame() < _primary->region()->position()) {
3554 /* backwards drag of the left edge - not usable */
3558 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3560 float percentage = (double) newlen / (double) _primary->region()->length();
3562 #ifndef USE_RUBBERBAND
3563 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3564 if (_primary->region()->data_type() == DataType::AUDIO) {
3565 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3569 if (!_editor->get_selection().regions.empty()) {
3570 /* primary will already be included in the selection, and edit
3571 group shared editing will propagate selection across
3572 equivalent regions, so just use the current region
3576 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3577 error << _("An error occurred while executing time stretch operation") << endmsg;
3583 TimeFXDrag::aborted (bool)
3585 _primary->get_time_axis_view().hide_timestretch ();
3588 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3591 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3595 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3597 Drag::start_grab (event);
3601 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3603 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3607 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3609 if (movement_occurred && _editor->session()) {
3610 /* make sure we stop */
3611 _editor->session()->request_transport_speed (0.0);
3616 ScrubDrag::aborted (bool)
3621 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3626 , _original_pointer_time_axis (-1)
3627 , _last_pointer_time_axis (-1)
3628 , _time_selection_at_start (!_editor->get_selection().time.empty())
3630 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3632 if (_time_selection_at_start) {
3633 start_at_start = _editor->get_selection().time.start();
3634 end_at_start = _editor->get_selection().time.end_frame();
3639 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3641 if (_editor->session() == 0) {
3645 Gdk::Cursor* cursor = 0;
3647 switch (_operation) {
3648 case CreateSelection:
3649 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3654 cursor = _editor->cursors()->selector;
3655 Drag::start_grab (event, cursor);
3658 case SelectionStartTrim:
3659 if (_editor->clicked_axisview) {
3660 _editor->clicked_axisview->order_selection_trims (_item, true);
3662 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3665 case SelectionEndTrim:
3666 if (_editor->clicked_axisview) {
3667 _editor->clicked_axisview->order_selection_trims (_item, false);
3669 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3673 Drag::start_grab (event, cursor);
3676 case SelectionExtend:
3677 Drag::start_grab (event, cursor);
3681 if (_operation == SelectionMove) {
3682 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3684 show_verbose_cursor_time (adjusted_current_frame (event));
3687 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3691 SelectionDrag::setup_pointer_frame_offset ()
3693 switch (_operation) {
3694 case CreateSelection:
3695 _pointer_frame_offset = 0;
3698 case SelectionStartTrim:
3700 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3703 case SelectionEndTrim:
3704 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3707 case SelectionExtend:
3713 SelectionDrag::motion (GdkEvent* event, bool first_move)
3715 framepos_t start = 0;
3717 framecnt_t length = 0;
3718 framecnt_t distance = 0;
3720 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3721 if (pending_time_axis.first == 0) {
3725 framepos_t const pending_position = adjusted_current_frame (event);
3727 /* only alter selection if things have changed */
3729 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3733 switch (_operation) {
3734 case CreateSelection:
3736 framepos_t grab = grab_frame ();
3739 grab = adjusted_current_frame (event, false);
3740 if (grab < pending_position) {
3741 _editor->snap_to (grab, -1);
3743 _editor->snap_to (grab, 1);
3747 if (pending_position < grab) {
3748 start = pending_position;
3751 end = pending_position;
3755 /* first drag: Either add to the selection
3756 or create a new selection
3762 /* adding to the selection */
3763 _editor->set_selected_track_as_side_effect (Selection::Add);
3764 //_editor->selection->add (_editor->clicked_axisview);
3765 _editor->clicked_selection = _editor->selection->add (start, end);
3770 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3771 //_editor->selection->set (_editor->clicked_axisview);
3772 _editor->set_selected_track_as_side_effect (Selection::Set);
3775 _editor->clicked_selection = _editor->selection->set (start, end);
3779 /* select the track that we're in */
3780 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3781 // _editor->set_selected_track_as_side_effect (Selection::Add);
3782 _editor->selection->add (pending_time_axis.first);
3783 _added_time_axes.push_back (pending_time_axis.first);
3786 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3787 tracks that we selected in the first place.
3790 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3791 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3793 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3794 while (i != _added_time_axes.end()) {
3796 list<TimeAxisView*>::iterator tmp = i;
3799 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3800 _editor->selection->remove (*i);
3801 _added_time_axes.remove (*i);
3810 case SelectionStartTrim:
3812 start = _editor->selection->time[_editor->clicked_selection].start;
3813 end = _editor->selection->time[_editor->clicked_selection].end;
3815 if (pending_position > end) {
3818 start = pending_position;
3822 case SelectionEndTrim:
3824 start = _editor->selection->time[_editor->clicked_selection].start;
3825 end = _editor->selection->time[_editor->clicked_selection].end;
3827 if (pending_position < start) {
3830 end = pending_position;
3837 start = _editor->selection->time[_editor->clicked_selection].start;
3838 end = _editor->selection->time[_editor->clicked_selection].end;
3840 length = end - start;
3841 distance = pending_position - start;
3842 start = pending_position;
3843 _editor->snap_to (start);
3845 end = start + length;
3849 case SelectionExtend:
3853 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
3854 _editor->start_canvas_autoscroll (1, 0);
3858 switch (_operation) {
3860 if (_time_selection_at_start) {
3861 _editor->selection->move_time (distance);
3865 _editor->selection->replace (_editor->clicked_selection, start, end);
3869 if (_operation == SelectionMove) {
3870 show_verbose_cursor_time(start);
3872 show_verbose_cursor_time(pending_position);
3877 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3879 Session* s = _editor->session();
3881 if (movement_occurred) {
3882 motion (event, false);
3883 /* XXX this is not object-oriented programming at all. ick */
3884 if (_editor->selection->time.consolidate()) {
3885 _editor->selection->TimeChanged ();
3888 /* XXX what if its a music time selection? */
3890 if ( s->get_play_range() && s->transport_rolling() ) {
3891 s->request_play_range (&_editor->selection->time, true);
3893 if (Config->get_always_play_range() && !s->transport_rolling()) {
3894 s->request_locate (_editor->get_selection().time.start());
3900 /* just a click, no pointer movement.
3903 if (_operation == SelectionExtend) {
3904 if (_time_selection_at_start) {
3905 framepos_t pos = adjusted_current_frame (event, false);
3906 framepos_t start = min (pos, start_at_start);
3907 framepos_t end = max (pos, end_at_start);
3908 _editor->selection->set (start, end);
3911 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3912 if (_editor->clicked_selection) {
3913 _editor->selection->remove (_editor->clicked_selection);
3916 if (!_editor->clicked_selection) {
3917 _editor->selection->clear_time();
3922 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3923 _editor->selection->set (_editor->clicked_axisview);
3926 if (s && s->get_play_range () && s->transport_rolling()) {
3927 s->request_stop (false, false);
3932 _editor->stop_canvas_autoscroll ();
3933 _editor->clicked_selection = 0;
3937 SelectionDrag::aborted (bool)
3942 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3947 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3949 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
3950 ArdourCanvas::Rect (0.0, 0.0, 0.0,
3951 physical_screen_height (_editor->get_window())));
3952 _drag_rect->hide ();
3954 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3955 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3959 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3961 if (_editor->session() == 0) {
3965 Gdk::Cursor* cursor = 0;
3967 if (!_editor->temp_location) {
3968 _editor->temp_location = new Location (*_editor->session());
3971 switch (_operation) {
3972 case CreateRangeMarker:
3973 case CreateTransportMarker:
3974 case CreateCDMarker:
3976 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3981 cursor = _editor->cursors()->selector;
3985 Drag::start_grab (event, cursor);
3987 show_verbose_cursor_time (adjusted_current_frame (event));
3991 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3993 framepos_t start = 0;
3995 ArdourCanvas::Rectangle *crect;
3997 switch (_operation) {
3998 case CreateRangeMarker:
3999 crect = _editor->range_bar_drag_rect;
4001 case CreateTransportMarker:
4002 crect = _editor->transport_bar_drag_rect;
4004 case CreateCDMarker:
4005 crect = _editor->cd_marker_bar_drag_rect;
4008 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4013 framepos_t const pf = adjusted_current_frame (event);
4015 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4016 framepos_t grab = grab_frame ();
4017 _editor->snap_to (grab);
4019 if (pf < grab_frame()) {
4027 /* first drag: Either add to the selection
4028 or create a new selection.
4033 _editor->temp_location->set (start, end);
4037 update_item (_editor->temp_location);
4039 //_drag_rect->raise_to_top();
4044 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
4045 _editor->start_canvas_autoscroll (1, 0);
4049 _editor->temp_location->set (start, end);
4051 double x1 = _editor->sample_to_pixel (start);
4052 double x2 = _editor->sample_to_pixel (end);
4056 update_item (_editor->temp_location);
4059 show_verbose_cursor_time (pf);
4064 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4066 Location * newloc = 0;
4070 if (movement_occurred) {
4071 motion (event, false);
4074 switch (_operation) {
4075 case CreateRangeMarker:
4076 case CreateCDMarker:
4078 _editor->begin_reversible_command (_("new range marker"));
4079 XMLNode &before = _editor->session()->locations()->get_state();
4080 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4081 if (_operation == CreateCDMarker) {
4082 flags = Location::IsRangeMarker | Location::IsCDMarker;
4083 _editor->cd_marker_bar_drag_rect->hide();
4086 flags = Location::IsRangeMarker;
4087 _editor->range_bar_drag_rect->hide();
4089 newloc = new Location (
4090 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4093 _editor->session()->locations()->add (newloc, true);
4094 XMLNode &after = _editor->session()->locations()->get_state();
4095 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4096 _editor->commit_reversible_command ();
4100 case CreateTransportMarker:
4101 // popup menu to pick loop or punch
4102 _editor->new_transport_marker_context_menu (&event->button, _item);
4106 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4108 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
4113 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4115 if (end == max_framepos) {
4116 end = _editor->session()->current_end_frame ();
4119 if (start == max_framepos) {
4120 start = _editor->session()->current_start_frame ();
4123 switch (_editor->mouse_mode) {
4125 /* find the two markers on either side and then make the selection from it */
4126 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4130 /* find the two markers on either side of the click and make the range out of it */
4131 _editor->selection->set (start, end);
4140 _editor->stop_canvas_autoscroll ();
4144 RangeMarkerBarDrag::aborted (bool)
4150 RangeMarkerBarDrag::update_item (Location* location)
4152 double const x1 = _editor->sample_to_pixel (location->start());
4153 double const x2 = _editor->sample_to_pixel (location->end());
4155 _drag_rect->set_x0 (x1);
4156 _drag_rect->set_x1 (x2);
4159 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4163 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4167 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4169 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4170 Drag::start_grab (event, _editor->cursors()->zoom_out);
4173 Drag::start_grab (event, _editor->cursors()->zoom_in);
4177 show_verbose_cursor_time (adjusted_current_frame (event));
4181 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4186 framepos_t const pf = adjusted_current_frame (event);
4188 framepos_t grab = grab_frame ();
4189 _editor->snap_to_with_modifier (grab, event);
4191 /* base start and end on initial click position */
4203 _editor->zoom_rect->show();
4204 _editor->zoom_rect->raise_to_top();
4207 _editor->reposition_zoom_rect(start, end);
4209 show_verbose_cursor_time (pf);
4214 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4216 if (movement_occurred) {
4217 motion (event, false);
4219 if (grab_frame() < last_pointer_frame()) {
4220 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4222 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4225 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4226 _editor->tav_zoom_step (_zoom_out);
4228 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4232 _editor->zoom_rect->hide();
4236 MouseZoomDrag::aborted (bool)
4238 _editor->zoom_rect->hide ();
4241 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4243 , _cumulative_dx (0)
4244 , _cumulative_dy (0)
4246 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4248 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4250 _region = &_primary->region_view ();
4251 _note_height = _region->midi_stream_view()->note_height ();
4255 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4257 Drag::start_grab (event);
4259 if (!(_was_selected = _primary->selected())) {
4261 /* tertiary-click means extend selection - we'll do that on button release,
4262 so don't add it here, because otherwise we make it hard to figure
4263 out the "extend-to" range.
4266 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4269 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4272 _region->note_selected (_primary, true);
4274 _region->unique_select (_primary);
4280 /** @return Current total drag x change in frames */
4282 NoteDrag::total_dx () const
4285 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4287 /* primary note time */
4288 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4290 /* new time of the primary note in session frames */
4291 frameoffset_t st = n + dx;
4293 framepos_t const rp = _region->region()->position ();
4295 /* prevent the note being dragged earlier than the region's position */
4298 /* snap and return corresponding delta */
4299 return _region->snap_frame_to_frame (st - rp) + rp - n;
4302 /** @return Current total drag y change in note number */
4304 NoteDrag::total_dy () const
4306 MidiStreamView* msv = _region->midi_stream_view ();
4307 double const y = _region->midi_view()->y_position ();
4308 /* new current note */
4309 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4311 n = max (msv->lowest_note(), n);
4312 n = min (msv->highest_note(), n);
4313 /* and work out delta */
4314 return n - msv->y_to_note (grab_y() - y);
4318 NoteDrag::motion (GdkEvent *, bool)
4320 /* Total change in x and y since the start of the drag */
4321 frameoffset_t const dx = total_dx ();
4322 int8_t const dy = total_dy ();
4324 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4325 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4326 double const tdy = -dy * _note_height - _cumulative_dy;
4329 _cumulative_dx += tdx;
4330 _cumulative_dy += tdy;
4332 int8_t note_delta = total_dy();
4334 _region->move_selection (tdx, tdy, note_delta);
4336 /* the new note value may be the same as the old one, but we
4337 * don't know what that means because the selection may have
4338 * involved more than one note and we might be doing something
4339 * odd with them. so show the note value anyway, always.
4343 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4345 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4346 (int) floor (new_note));
4348 show_verbose_cursor_text (buf);
4353 NoteDrag::finished (GdkEvent* ev, bool moved)
4356 /* no motion - select note */
4358 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4359 _editor->current_mouse_mode() == Editing::MouseDraw) {
4361 if (_was_selected) {
4362 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4364 _region->note_deselected (_primary);
4367 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4368 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4370 if (!extend && !add && _region->selection_size() > 1) {
4371 _region->unique_select (_primary);
4372 } else if (extend) {
4373 _region->note_selected (_primary, true, true);
4375 /* it was added during button press */
4380 _region->note_dropped (_primary, total_dx(), total_dy());
4385 NoteDrag::aborted (bool)
4390 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4391 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4392 : Drag (editor, atv->base_item ())
4394 , _nothing_to_drag (false)
4396 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4397 y_origin = atv->y_position();
4398 setup (atv->lines ());
4401 /** Make an AutomationRangeDrag for region gain lines */
4402 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4403 : Drag (editor, rv->get_canvas_group ())
4405 , _nothing_to_drag (false)
4407 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4409 list<boost::shared_ptr<AutomationLine> > lines;
4410 lines.push_back (rv->get_gain_line ());
4411 y_origin = rv->get_time_axis_view().y_position();
4415 /** @param lines AutomationLines to drag.
4416 * @param offset Offset from the session start to the points in the AutomationLines.
4419 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4421 /* find the lines that overlap the ranges being dragged */
4422 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4423 while (i != lines.end ()) {
4424 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4427 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4429 /* check this range against all the AudioRanges that we are using */
4430 list<AudioRange>::const_iterator k = _ranges.begin ();
4431 while (k != _ranges.end()) {
4432 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4438 /* add it to our list if it overlaps at all */
4439 if (k != _ranges.end()) {
4444 _lines.push_back (n);
4450 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4454 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4456 return 1.0 - ((global_y - y_origin) / line->height());
4460 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4462 Drag::start_grab (event, cursor);
4464 /* Get line states before we start changing things */
4465 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4466 i->state = &i->line->get_state ();
4467 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4470 if (_ranges.empty()) {
4472 /* No selected time ranges: drag all points */
4473 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4474 uint32_t const N = i->line->npoints ();
4475 for (uint32_t j = 0; j < N; ++j) {
4476 i->points.push_back (i->line->nth (j));
4482 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4484 framecnt_t const half = (i->start + i->end) / 2;
4486 /* find the line that this audio range starts in */
4487 list<Line>::iterator j = _lines.begin();
4488 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4492 if (j != _lines.end()) {
4493 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4495 /* j is the line that this audio range starts in; fade into it;
4496 64 samples length plucked out of thin air.
4499 framepos_t a = i->start + 64;
4504 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4505 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4507 the_list->add (p, the_list->eval (p));
4508 the_list->add (q, the_list->eval (q));
4511 /* same thing for the end */
4514 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4518 if (j != _lines.end()) {
4519 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4521 /* j is the line that this audio range starts in; fade out of it;
4522 64 samples length plucked out of thin air.
4525 framepos_t b = i->end - 64;
4530 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4531 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4533 the_list->add (p, the_list->eval (p));
4534 the_list->add (q, the_list->eval (q));
4538 _nothing_to_drag = true;
4540 /* Find all the points that should be dragged and put them in the relevant
4541 points lists in the Line structs.
4544 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4546 uint32_t const N = i->line->npoints ();
4547 for (uint32_t j = 0; j < N; ++j) {
4549 /* here's a control point on this line */
4550 ControlPoint* p = i->line->nth (j);
4551 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4553 /* see if it's inside a range */
4554 list<AudioRange>::const_iterator k = _ranges.begin ();
4555 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4559 if (k != _ranges.end()) {
4560 /* dragging this point */
4561 _nothing_to_drag = false;
4562 i->points.push_back (p);
4568 if (_nothing_to_drag) {
4572 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4573 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4578 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4580 if (_nothing_to_drag) {
4584 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4585 float const f = y_fraction (l->line, _drags->current_pointer_y());
4586 /* we are ignoring x position for this drag, so we can just pass in anything */
4588 l->line->drag_motion (0, f, true, false, ignored);
4589 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4594 AutomationRangeDrag::finished (GdkEvent* event, bool)
4596 if (_nothing_to_drag) {
4600 motion (event, false);
4601 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4602 i->line->end_drag (false, 0);
4605 _editor->session()->commit_reversible_command ();
4609 AutomationRangeDrag::aborted (bool)
4611 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4616 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4619 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4620 layer = v->region()->layer ();
4621 initial_y = v->get_canvas_group()->position().y;
4622 initial_playlist = v->region()->playlist ();
4623 initial_position = v->region()->position ();
4624 initial_end = v->region()->position () + v->region()->length ();
4627 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4628 : Drag (e, i->canvas_item ())
4631 , _cumulative_dx (0)
4633 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4634 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4639 PatchChangeDrag::motion (GdkEvent* ev, bool)
4641 framepos_t f = adjusted_current_frame (ev);
4642 boost::shared_ptr<Region> r = _region_view->region ();
4643 f = max (f, r->position ());
4644 f = min (f, r->last_frame ());
4646 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4647 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4648 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4649 _cumulative_dx = dxu;
4653 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4655 if (!movement_occurred) {
4659 boost::shared_ptr<Region> r (_region_view->region ());
4660 framepos_t f = adjusted_current_frame (ev);
4661 f = max (f, r->position ());
4662 f = min (f, r->last_frame ());
4664 _region_view->move_patch_change (
4666 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4671 PatchChangeDrag::aborted (bool)
4673 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4677 PatchChangeDrag::setup_pointer_frame_offset ()
4679 boost::shared_ptr<Region> region = _region_view->region ();
4680 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4683 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4684 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4691 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4693 framepos_t const p = _region_view->region()->position ();
4694 double const y = _region_view->midi_view()->y_position ();
4696 x1 = max ((framepos_t) 0, x1 - p);
4697 x2 = max ((framepos_t) 0, x2 - p);
4698 y1 = max (0.0, y1 - y);
4699 y2 = max (0.0, y2 - y);
4701 _region_view->update_drag_selection (
4702 _editor->sample_to_pixel (x1),
4703 _editor->sample_to_pixel (x2),
4706 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4711 MidiRubberbandSelectDrag::deselect_things ()
4716 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4717 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4720 _vertical_only = true;
4724 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4726 double const y = _region_view->midi_view()->y_position ();
4728 y1 = max (0.0, y1 - y);
4729 y2 = max (0.0, y2 - y);
4731 _region_view->update_vertical_drag_selection (
4734 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4739 MidiVerticalSelectDrag::deselect_things ()
4744 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4745 : RubberbandSelectDrag (e, i)
4751 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4753 if (drag_in_progress) {
4754 /* We just want to select things at the end of the drag, not during it */
4758 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4760 _editor->begin_reversible_command (_("rubberband selection"));
4761 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4762 _editor->commit_reversible_command ();
4766 EditorRubberbandSelectDrag::deselect_things ()
4768 if (!getenv("ARDOUR_SAE")) {
4769 _editor->selection->clear_tracks();
4771 _editor->selection->clear_regions();
4772 _editor->selection->clear_points ();
4773 _editor->selection->clear_lines ();
4776 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4784 NoteCreateDrag::~NoteCreateDrag ()
4790 NoteCreateDrag::grid_frames (framepos_t t) const
4793 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4798 return _region_view->region_beats_to_region_frames (grid_beats);
4802 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4804 Drag::start_grab (event, cursor);
4806 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4808 framepos_t pf = _drags->current_pointer_frame ();
4809 framecnt_t const g = grid_frames (pf);
4811 /* Hack so that we always snap to the note that we are over, instead of snapping
4812 to the next one if we're more than halfway through the one we're over.
4814 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4818 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4820 MidiStreamView* sv = _region_view->midi_stream_view ();
4821 double const x = _editor->sample_to_pixel (_note[0]);
4822 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4824 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4825 _drag_rect->set_outline_what (0xff);
4826 _drag_rect->set_outline_color (0xffffff99);
4827 _drag_rect->set_fill_color (0xffffff66);
4831 NoteCreateDrag::motion (GdkEvent* event, bool)
4833 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4834 double const x = _editor->sample_to_pixel (_note[1]);
4835 if (_note[1] > _note[0]) {
4836 _drag_rect->set_x1 (x);
4838 _drag_rect->set_x0 (x);
4843 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4845 if (!had_movement) {
4849 framepos_t const start = min (_note[0], _note[1]);
4850 framecnt_t length = (framecnt_t) fabs (_note[0] - _note[1]);
4852 framecnt_t const g = grid_frames (start);
4853 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4855 if (_editor->snap_mode() == SnapNormal && length < g) {
4856 length = g - one_tick;
4859 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4861 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
4865 NoteCreateDrag::y_to_region (double y) const
4868 _region_view->get_canvas_group()->canvas_to_item (x, y);
4873 NoteCreateDrag::aborted (bool)
4878 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4883 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
4887 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4889 Drag::start_grab (event, cursor);
4893 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4899 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4902 distance = _drags->current_pointer_x() - grab_x();
4903 len = ar->fade_in()->back()->when;
4905 distance = grab_x() - _drags->current_pointer_x();
4906 len = ar->fade_out()->back()->when;
4909 /* how long should it be ? */
4911 new_length = len + _editor->pixel_to_sample (distance);
4913 /* now check with the region that this is legal */
4915 new_length = ar->verify_xfade_bounds (new_length, start);
4918 arv->reset_fade_in_shape_width (ar, new_length);
4920 arv->reset_fade_out_shape_width (ar, new_length);
4925 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
4931 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4934 distance = _drags->current_pointer_x() - grab_x();
4935 len = ar->fade_in()->back()->when;
4937 distance = grab_x() - _drags->current_pointer_x();
4938 len = ar->fade_out()->back()->when;
4941 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
4943 _editor->begin_reversible_command ("xfade trim");
4944 ar->playlist()->clear_owned_changes ();
4947 ar->set_fade_in_length (new_length);
4949 ar->set_fade_out_length (new_length);
4952 /* Adjusting the xfade may affect other regions in the playlist, so we need
4953 to get undo Commands from the whole playlist rather than just the
4957 vector<Command*> cmds;
4958 ar->playlist()->rdiff (cmds);
4959 _editor->session()->add_commands (cmds);
4960 _editor->commit_reversible_command ();
4965 CrossfadeEdgeDrag::aborted (bool)
4968 arv->redraw_start_xfade ();
4970 arv->redraw_end_xfade ();