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->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->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::have_item (ArdourCanvas::Item* i) const
184 list<Drag*>::const_iterator j = _drags.begin ();
185 while (j != _drags.end() && (*j)->item () != i) {
189 return j != _drags.end ();
192 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
195 , _pointer_frame_offset (0)
196 , _move_threshold_passed (false)
197 , _raw_grab_frame (0)
199 , _last_pointer_frame (0)
205 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
218 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
220 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
222 if (Keyboard::is_button2_event (&event->button)) {
223 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
224 _y_constrained = true;
225 _x_constrained = false;
227 _y_constrained = false;
228 _x_constrained = true;
231 _x_constrained = false;
232 _y_constrained = false;
235 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
236 setup_pointer_frame_offset ();
237 _grab_frame = adjusted_frame (_raw_grab_frame, event);
238 _last_pointer_frame = _grab_frame;
239 _last_pointer_x = _grab_x;
240 _last_pointer_y = _grab_y;
246 /* CAIROCANVAS need a variant here that passes *cursor */
251 if (_editor->session() && _editor->session()->transport_rolling()) {
254 _was_rolling = false;
257 switch (_editor->snap_type()) {
258 case SnapToRegionStart:
259 case SnapToRegionEnd:
260 case SnapToRegionSync:
261 case SnapToRegionBoundary:
262 _editor->build_region_boundary_cache ();
269 /** Call to end a drag `successfully'. Ungrabs item and calls
270 * subclass' finished() method.
272 * @param event GDK event, or 0.
273 * @return true if some movement occurred, otherwise false.
276 Drag::end_grab (GdkEvent* event)
278 _editor->stop_canvas_autoscroll ();
282 finished (event, _move_threshold_passed);
284 _editor->verbose_cursor()->hide ();
286 return _move_threshold_passed;
290 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
294 if (f > _pointer_frame_offset) {
295 pos = f - _pointer_frame_offset;
299 _editor->snap_to_with_modifier (pos, event);
306 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
308 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
312 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
314 /* check to see if we have moved in any way that matters since the last motion event */
315 if (_move_threshold_passed &&
316 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
317 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
321 pair<framecnt_t, int> const threshold = move_threshold ();
323 bool const old_move_threshold_passed = _move_threshold_passed;
325 if (!from_autoscroll && !_move_threshold_passed) {
327 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
328 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
330 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
333 if (active (_editor->mouse_mode) && _move_threshold_passed) {
335 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
336 if (!from_autoscroll) {
337 bool const moving_left = _drags->current_pointer_x() < _last_pointer_x;
338 bool const moving_up = _drags->current_pointer_y() < _last_pointer_y;
339 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), moving_left, moving_up);
342 motion (event, _move_threshold_passed != old_move_threshold_passed);
344 _last_pointer_x = _drags->current_pointer_x ();
345 _last_pointer_y = _drags->current_pointer_y ();
346 _last_pointer_frame = adjusted_current_frame (event);
354 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
362 aborted (_move_threshold_passed);
364 _editor->stop_canvas_autoscroll ();
365 _editor->verbose_cursor()->hide ();
369 Drag::show_verbose_cursor_time (framepos_t frame)
371 _editor->verbose_cursor()->set_time (
373 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
374 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value()
377 _editor->verbose_cursor()->show ();
381 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
383 _editor->verbose_cursor()->show (xoffset);
385 _editor->verbose_cursor()->set_duration (
387 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
388 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value()
393 Drag::show_verbose_cursor_text (string const & text)
395 _editor->verbose_cursor()->show ();
397 _editor->verbose_cursor()->set (
399 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
400 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value()
404 boost::shared_ptr<Region>
405 Drag::add_midi_region (MidiTimeAxisView* view)
407 if (_editor->session()) {
408 const TempoMap& map (_editor->session()->tempo_map());
409 framecnt_t pos = grab_frame();
410 const Meter& m = map.meter_at (pos);
411 /* not that the frame rate used here can be affected by pull up/down which
414 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
415 return view->add_region (grab_frame(), len, true);
418 return boost::shared_ptr<Region>();
421 struct EditorOrderTimeAxisViewSorter {
422 bool operator() (TimeAxisView* a, TimeAxisView* b) {
423 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
424 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
426 return ra->route()->order_key (EditorSort) < rb->route()->order_key (EditorSort);
430 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
434 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
436 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
437 as some of the regions we are dragging may be on such tracks.
440 TrackViewList track_views = _editor->track_views;
441 track_views.sort (EditorOrderTimeAxisViewSorter ());
443 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
444 _time_axis_views.push_back (*i);
446 TimeAxisView::Children children_list = (*i)->get_child_list ();
447 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
448 _time_axis_views.push_back (j->get());
452 /* the list of views can be empty at this point if this is a region list-insert drag
455 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
456 _views.push_back (DraggingView (*i, this));
459 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
463 RegionDrag::region_going_away (RegionView* v)
465 list<DraggingView>::iterator i = _views.begin ();
466 while (i != _views.end() && i->view != v) {
470 if (i != _views.end()) {
475 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
476 * or -1 if it is not found.
479 RegionDrag::find_time_axis_view (TimeAxisView* t) const
482 int const N = _time_axis_views.size ();
483 while (i < N && _time_axis_views[i] != t) {
494 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
495 : RegionDrag (e, i, p, v),
504 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
506 Drag::start_grab (event, cursor);
508 show_verbose_cursor_time (_last_frame_position);
510 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
511 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
512 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
516 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
518 /* compute the amount of pointer motion in frames, and where
519 the region would be if we moved it by that much.
521 *pending_region_position = adjusted_current_frame (event);
523 framepos_t sync_frame;
524 framecnt_t sync_offset;
527 sync_offset = _primary->region()->sync_offset (sync_dir);
529 /* we don't handle a sync point that lies before zero.
531 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
533 sync_frame = *pending_region_position + (sync_dir*sync_offset);
535 _editor->snap_to_with_modifier (sync_frame, event);
537 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
540 *pending_region_position = _last_frame_position;
543 if (*pending_region_position > max_framepos - _primary->region()->length()) {
544 *pending_region_position = _last_frame_position;
549 /* in locked edit mode, reverse the usual meaning of _x_constrained */
550 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
552 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
554 /* x movement since last time (in pixels) */
555 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_pixel;
557 /* total x movement */
558 framecnt_t total_dx = *pending_region_position;
559 if (regions_came_from_canvas()) {
560 total_dx = total_dx - grab_frame ();
563 /* check that no regions have gone off the start of the session */
564 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
565 if ((i->view->region()->position() + total_dx) < 0) {
567 *pending_region_position = _last_frame_position;
572 _last_frame_position = *pending_region_position;
579 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
581 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
582 int const n = i->time_axis_view + delta_track;
583 if (n < 0 || n >= int (_time_axis_views.size())) {
584 /* off the top or bottom track */
588 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
589 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
590 /* not a track, or the wrong type */
594 double const l = i->layer + delta_layer;
596 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
597 mode to allow the user to place a region below another on layer 0.
599 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
600 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
601 If it has, the layers will be munged later anyway, so it's ok.
607 /* all regions being dragged are ok with this change */
612 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
614 assert (!_views.empty ());
616 /* Find the TimeAxisView that the pointer is now over */
617 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
619 if (first_move && tv.first->view()->layer_display() == Stacked) {
620 tv.first->view()->set_layer_display (Expanded);
623 /* Bail early if we're not over a track */
624 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
625 if (!rtv || !rtv->is_track()) {
626 _editor->verbose_cursor()->hide ();
630 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
632 /* Here's the current pointer position in terms of time axis view and layer */
633 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
634 double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
636 /* Work out the change in x */
637 framepos_t pending_region_position;
638 double const x_delta = compute_x_delta (event, &pending_region_position);
640 /* Work out the change in y */
641 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
642 double delta_layer = current_pointer_layer - _last_pointer_layer;
644 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
645 /* this y movement is not allowed, so do no y movement this time */
646 delta_time_axis_view = 0;
650 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
651 /* haven't reached next snap point, and we're not switching
652 trackviews nor layers. nothing to do.
657 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
659 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
661 RegionView* rv = i->view;
663 if (rv->region()->locked()
664 #ifdef WITH_VIDEOTIMELINE
665 || rv->region()->video_locked()
675 /* Absolutely no idea why this is necessary, but it is; without
676 it, the region view disappears after the reparent.
678 _editor->update_canvas_now ();
680 /* Reparent to a non scrolling group so that we can keep the
681 region selection above all time axis views.
682 Reparenting means that we will have to move the region view
683 later, as the two parent groups have different coordinates.
686 rv->get_canvas_group()->reparent (_editor->_region_motion_group);
688 rv->fake_set_opaque (true);
691 /* If we have moved tracks, we'll fudge the layer delta so that the
692 region gets moved back onto layer 0 on its new track; this avoids
693 confusion when dragging regions from non-zero layers onto different
696 double this_delta_layer = delta_layer;
697 if (delta_time_axis_view != 0) {
698 this_delta_layer = - i->layer;
701 /* The TimeAxisView that this region is now on */
702 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
704 /* Ensure it is moved from stacked -> expanded if appropriate */
705 if (tv->view()->layer_display() == Stacked) {
706 tv->view()->set_layer_display (Expanded);
709 /* We're only allowed to go -ve in layer on Expanded views */
710 if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
711 this_delta_layer = - i->layer;
715 rv->set_height (tv->view()->child_height ());
717 /* Update show/hidden status as the region view may have come from a hidden track,
718 or have moved to one.
721 rv->get_canvas_group()->hide ();
723 rv->get_canvas_group()->show ();
726 /* Update the DraggingView */
727 i->time_axis_view += delta_time_axis_view;
728 i->layer += this_delta_layer;
731 _editor->mouse_brush_insert_region (rv, pending_region_position);
736 /* Get the y coordinate of the top of the track that this region is now on */
737 tv->canvas_display()->item_to_canvas (x, y);
738 y += _editor->get_trackview_group_vertical_offset();
740 /* And adjust for the layer that it should be on */
741 StreamView* cv = tv->view ();
742 switch (cv->layer_display ()) {
746 y += (cv->layers() - i->layer - 1) * cv->child_height ();
749 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
753 /* Now move the region view */
754 rv->move (x_delta, y - rv->get_canvas_group()->position().y);
757 } /* foreach region */
759 _total_x_delta += x_delta;
761 if (x_delta != 0 && !_brushing) {
762 show_verbose_cursor_time (_last_frame_position);
765 _last_pointer_time_axis_view += delta_time_axis_view;
766 _last_pointer_layer += delta_layer;
770 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
772 if (_copy && first_move) {
774 /* duplicate the regionview(s) and region(s) */
776 list<DraggingView> new_regionviews;
778 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
780 RegionView* rv = i->view;
781 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
782 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
784 const boost::shared_ptr<const Region> original = rv->region();
785 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
786 region_copy->set_position (original->position());
790 boost::shared_ptr<AudioRegion> audioregion_copy
791 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
793 nrv = new AudioRegionView (*arv, audioregion_copy);
795 boost::shared_ptr<MidiRegion> midiregion_copy
796 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
797 nrv = new MidiRegionView (*mrv, midiregion_copy);
802 nrv->get_canvas_group()->show ();
803 new_regionviews.push_back (DraggingView (nrv, this));
805 /* swap _primary to the copy */
807 if (rv == _primary) {
811 /* ..and deselect the one we copied */
813 rv->set_selected (false);
816 if (!new_regionviews.empty()) {
818 /* reflect the fact that we are dragging the copies */
820 _views = new_regionviews;
822 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
825 sync the canvas to what we think is its current state
826 without it, the canvas seems to
827 "forget" to update properly after the upcoming reparent()
828 ..only if the mouse is in rapid motion at the time of the grab.
829 something to do with regionview creation taking so long?
831 _editor->update_canvas_now();
835 RegionMotionDrag::motion (event, first_move);
839 RegionMotionDrag::finished (GdkEvent *, bool)
841 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
846 if ((*i)->view()->layer_display() == Expanded) {
847 (*i)->view()->set_layer_display (Stacked);
853 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
855 RegionMotionDrag::finished (ev, movement_occurred);
857 if (!movement_occurred) {
862 /* reverse this here so that we have the correct logic to finalize
866 if (Config->get_edit_mode() == Lock) {
867 _x_constrained = !_x_constrained;
870 assert (!_views.empty ());
872 /* We might have hidden region views so that they weren't visible during the drag
873 (when they have been reparented). Now everything can be shown again, as region
874 views are back in their track parent groups.
876 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
877 i->view->get_canvas_group()->show ();
880 bool const changed_position = (_last_frame_position != _primary->region()->position());
881 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
882 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
884 _editor->update_canvas_now ();
904 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
908 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
910 RegionSelection new_views;
911 PlaylistSet modified_playlists;
912 list<RegionView*> views_to_delete;
915 /* all changes were made during motion event handlers */
917 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
921 _editor->commit_reversible_command ();
925 if (_x_constrained) {
926 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
928 _editor->begin_reversible_command (Operations::region_copy);
931 /* insert the regions into their new playlists */
932 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
934 if (i->view->region()->locked()
935 #ifdef WITH_VIDEOTIMELINE
936 || i->view->region()->video_locked()
944 if (changed_position && !_x_constrained) {
945 where = i->view->region()->position() - drag_delta;
947 where = i->view->region()->position();
950 RegionView* new_view = insert_region_into_playlist (
951 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
958 new_views.push_back (new_view);
960 /* we don't need the copied RegionView any more */
961 views_to_delete.push_back (i->view);
964 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
965 because when views are deleted they are automagically removed from _views, which messes
968 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
972 /* If we've created new regions either by copying or moving
973 to a new track, we want to replace the old selection with the new ones
976 if (new_views.size() > 0) {
977 _editor->selection->set (new_views);
980 /* write commands for the accumulated diffs for all our modified playlists */
981 add_stateful_diff_commands_for_playlists (modified_playlists);
983 _editor->commit_reversible_command ();
987 RegionMoveDrag::finished_no_copy (
988 bool const changed_position,
989 bool const changed_tracks,
990 framecnt_t const drag_delta
993 RegionSelection new_views;
994 PlaylistSet modified_playlists;
995 PlaylistSet frozen_playlists;
996 set<RouteTimeAxisView*> views_to_update;
999 /* all changes were made during motion event handlers */
1000 _editor->commit_reversible_command ();
1004 if (_x_constrained) {
1005 _editor->begin_reversible_command (_("fixed time region drag"));
1007 _editor->begin_reversible_command (Operations::region_drag);
1010 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1012 RegionView* rv = i->view;
1014 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1015 double const dest_layer = i->layer;
1017 if (rv->region()->locked()
1018 #ifdef WITH_VIDEOTIMELINE
1019 || rv->region()->video_locked()
1026 views_to_update.insert (dest_rtv);
1030 if (changed_position && !_x_constrained) {
1031 where = rv->region()->position() - drag_delta;
1033 where = rv->region()->position();
1036 if (changed_tracks) {
1038 /* insert into new playlist */
1040 RegionView* new_view = insert_region_into_playlist (
1041 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1044 if (new_view == 0) {
1049 new_views.push_back (new_view);
1051 /* remove from old playlist */
1053 /* the region that used to be in the old playlist is not
1054 moved to the new one - we use a copy of it. as a result,
1055 any existing editor for the region should no longer be
1058 rv->hide_region_editor();
1059 rv->fake_set_opaque (false);
1061 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1065 rv->region()->clear_changes ();
1068 motion on the same track. plonk the previously reparented region
1069 back to its original canvas group (its streamview).
1070 No need to do anything for copies as they are fake regions which will be deleted.
1073 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1074 rv->get_canvas_group()->set_y_position (i->initial_y);
1077 /* just change the model */
1079 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1081 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1082 playlist->set_layer (rv->region(), dest_layer);
1085 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1087 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1090 playlist->freeze ();
1093 /* this movement may result in a crossfade being modified, so we need to get undo
1094 data from the playlist as well as the region.
1097 r = modified_playlists.insert (playlist);
1099 playlist->clear_changes ();
1102 rv->region()->set_position (where);
1104 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1107 if (changed_tracks) {
1109 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1110 was selected in all of them, then removing it from a playlist will have removed all
1111 trace of it from _views (i.e. there were N regions selected, we removed 1,
1112 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1113 corresponding regionview, and _views is now empty).
1115 This could have invalidated any and all iterators into _views.
1117 The heuristic we use here is: if the region selection is empty, break out of the loop
1118 here. if the region selection is not empty, then restart the loop because we know that
1119 we must have removed at least the region(view) we've just been working on as well as any
1120 that we processed on previous iterations.
1122 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1123 we can just iterate.
1127 if (_views.empty()) {
1138 /* If we've created new regions either by copying or moving
1139 to a new track, we want to replace the old selection with the new ones
1142 if (new_views.size() > 0) {
1143 _editor->selection->set (new_views);
1146 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1150 /* write commands for the accumulated diffs for all our modified playlists */
1151 add_stateful_diff_commands_for_playlists (modified_playlists);
1153 _editor->commit_reversible_command ();
1155 /* We have futzed with the layering of canvas items on our streamviews.
1156 If any region changed layer, this will have resulted in the stream
1157 views being asked to set up their region views, and all will be well.
1158 If not, we might now have badly-ordered region views. Ask the StreamViews
1159 involved to sort themselves out, just in case.
1162 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1163 (*i)->view()->playlist_layered ((*i)->track ());
1167 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1168 * @param region Region to remove.
1169 * @param playlist playlist To remove from.
1170 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1171 * that clear_changes () is only called once per playlist.
1174 RegionMoveDrag::remove_region_from_playlist (
1175 boost::shared_ptr<Region> region,
1176 boost::shared_ptr<Playlist> playlist,
1177 PlaylistSet& modified_playlists
1180 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1183 playlist->clear_changes ();
1186 playlist->remove_region (region);
1190 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1191 * clearing the playlist's diff history first if necessary.
1192 * @param region Region to insert.
1193 * @param dest_rtv Destination RouteTimeAxisView.
1194 * @param dest_layer Destination layer.
1195 * @param where Destination position.
1196 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1197 * that clear_changes () is only called once per playlist.
1198 * @return New RegionView, or 0 if no insert was performed.
1201 RegionMoveDrag::insert_region_into_playlist (
1202 boost::shared_ptr<Region> region,
1203 RouteTimeAxisView* dest_rtv,
1206 PlaylistSet& modified_playlists
1209 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1210 if (!dest_playlist) {
1214 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1215 _new_region_view = 0;
1216 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1218 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1219 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1221 dest_playlist->clear_changes ();
1224 dest_playlist->add_region (region, where);
1226 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1227 dest_playlist->set_layer (region, dest_layer);
1232 assert (_new_region_view);
1234 return _new_region_view;
1238 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1240 _new_region_view = rv;
1244 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1246 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1247 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1249 _editor->session()->add_command (c);
1258 RegionMoveDrag::aborted (bool movement_occurred)
1262 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1269 RegionMotionDrag::aborted (movement_occurred);
1274 RegionMotionDrag::aborted (bool)
1276 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1277 if ((*i)->view()->layer_display() == Expanded) {
1278 (*i)->view()->set_layer_display (Stacked);
1282 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1283 RegionView* rv = i->view;
1284 TimeAxisView* tv = &(rv->get_time_axis_view ());
1285 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1287 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1288 rv->get_canvas_group()->set_y_position (0);
1290 rv->fake_set_opaque (false);
1291 rv->move (-_total_x_delta, 0);
1292 rv->set_height (rtv->view()->child_height ());
1295 _editor->update_canvas_now ();
1298 /** @param b true to brush, otherwise false.
1299 * @param c true to make copies of the regions being moved, otherwise false.
1301 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1302 : RegionMotionDrag (e, i, p, v, b),
1305 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1308 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1309 if (rtv && rtv->is_track()) {
1310 speed = rtv->track()->speed ();
1313 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1317 RegionMoveDrag::setup_pointer_frame_offset ()
1319 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1322 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1323 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1325 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1327 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1328 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1330 _primary = v->view()->create_region_view (r, false, false);
1332 _primary->get_canvas_group()->show ();
1333 _primary->set_position (pos, 0);
1334 _views.push_back (DraggingView (_primary, this));
1336 _last_frame_position = pos;
1338 _item = _primary->get_canvas_group ();
1342 RegionInsertDrag::finished (GdkEvent *, bool)
1344 _editor->update_canvas_now ();
1346 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1348 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1349 _primary->get_canvas_group()->set_y_position (0);
1351 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1353 _editor->begin_reversible_command (Operations::insert_region);
1354 playlist->clear_changes ();
1355 playlist->add_region (_primary->region (), _last_frame_position);
1356 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1357 _editor->commit_reversible_command ();
1365 RegionInsertDrag::aborted (bool)
1372 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1373 : RegionMoveDrag (e, i, p, v, false, false)
1375 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1378 struct RegionSelectionByPosition {
1379 bool operator() (RegionView*a, RegionView* b) {
1380 return a->region()->position () < b->region()->position();
1385 RegionSpliceDrag::motion (GdkEvent* event, bool)
1387 /* Which trackview is this ? */
1389 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1390 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1392 /* The region motion is only processed if the pointer is over
1396 if (!tv || !tv->is_track()) {
1397 /* To make sure we hide the verbose canvas cursor when the mouse is
1398 not held over and audiotrack.
1400 _editor->verbose_cursor()->hide ();
1406 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1412 RegionSelection copy (_editor->selection->regions);
1414 RegionSelectionByPosition cmp;
1417 framepos_t const pf = adjusted_current_frame (event);
1419 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1421 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1427 boost::shared_ptr<Playlist> playlist;
1429 if ((playlist = atv->playlist()) == 0) {
1433 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1438 if (pf < (*i)->region()->last_frame() + 1) {
1442 if (pf > (*i)->region()->first_frame()) {
1448 playlist->shuffle ((*i)->region(), dir);
1453 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1455 RegionMoveDrag::finished (event, movement_occurred);
1459 RegionSpliceDrag::aborted (bool)
1464 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1466 _view (dynamic_cast<MidiTimeAxisView*> (v))
1468 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1474 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1477 _region = add_midi_region (_view);
1478 _view->playlist()->freeze ();
1481 framepos_t const f = adjusted_current_frame (event);
1482 if (f < grab_frame()) {
1483 _region->set_position (f);
1486 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1487 so that if this region is duplicated, its duplicate starts on
1488 a snap point rather than 1 frame after a snap point. Otherwise things get
1489 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1490 place snapped notes at the start of the region.
1493 framecnt_t const len = (framecnt_t) fabs (f - grab_frame () - 1);
1494 _region->set_length (len < 1 ? 1 : len);
1500 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1502 if (!movement_occurred) {
1503 add_midi_region (_view);
1505 _view->playlist()->thaw ();
1510 RegionCreateDrag::aborted (bool)
1513 _view->playlist()->thaw ();
1519 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1523 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1527 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1529 Gdk::Cursor* cursor;
1530 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1532 float x_fraction = cnote->mouse_x_fraction ();
1534 if (x_fraction > 0.0 && x_fraction < 0.25) {
1535 cursor = _editor->cursors()->left_side_trim;
1537 cursor = _editor->cursors()->right_side_trim;
1540 Drag::start_grab (event, cursor);
1542 region = &cnote->region_view();
1544 double const region_start = region->get_position_pixels();
1545 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1547 if (grab_x() <= middle_point) {
1548 cursor = _editor->cursors()->left_side_trim;
1551 cursor = _editor->cursors()->right_side_trim;
1557 if (event->motion.state & Keyboard::PrimaryModifier) {
1563 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1565 if (ms.size() > 1) {
1566 /* has to be relative, may make no sense otherwise */
1570 /* select this note; if it is already selected, preserve the existing selection,
1571 otherwise make this note the only one selected.
1573 region->note_selected (cnote, cnote->selected ());
1575 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1576 MidiRegionSelection::iterator next;
1579 (*r)->begin_resizing (at_front);
1585 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
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)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1596 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1598 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1599 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1600 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1602 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1607 NoteResizeDrag::aborted (bool)
1609 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1610 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1611 (*r)->abort_resizing ();
1615 #ifdef WITH_VIDEOTIMELINE
1617 AVDraggingView::AVDraggingView (RegionView* v)
1620 initial_position = v->region()->position ();
1623 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1626 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1628 /* create a list of regions to move along */
1629 #if 1 /* all reagions -- with video_locked() */
1631 TrackViewList empty;
1633 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1634 std::list<RegionView*> views = rs.by_layer();
1635 #else /* selected regions -- with video_locked() */
1636 std::list<RegionView*> views = _editor->selection->regions.by_layer();
1638 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1639 RegionView* rv = (*i);
1640 if (!rv->region()->video_locked()) {
1643 _views.push_back (AVDraggingView (rv));
1648 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1650 Drag::start_grab (event);
1651 if (_editor->session() == 0) {
1655 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1656 _max_backwards_drag = (
1657 ARDOUR_UI::instance()->video_timeline->get_duration()
1658 + ARDOUR_UI::instance()->video_timeline->get_offset()
1659 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1662 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1663 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1664 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1667 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1670 Timecode::Time timecode;
1671 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1672 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);
1673 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1674 _editor->verbose_cursor()->show ();
1678 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1680 if (_editor->session() == 0) {
1683 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1687 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1688 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(dt);
1690 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1691 dt = - _max_backwards_drag;
1694 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1695 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1697 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1698 RegionView* rv = i->view;
1699 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1702 _editor->update_canvas_now ();
1703 rv->fake_set_opaque (true);
1704 rv->region()->clear_changes ();
1705 rv->region()->suspend_property_changes();
1707 rv->region()->set_position(i->initial_position + dt);
1708 rv->region_changed(ARDOUR::Properties::position);
1711 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1712 Timecode::Time timecode;
1713 Timecode::Time timediff;
1715 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1716 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1717 snprintf (buf, sizeof (buf),
1718 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1719 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1720 , _("Video Start:"),
1721 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1723 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1725 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1726 _editor->verbose_cursor()->show ();
1730 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1732 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1736 if (!movement_occurred || ! _editor->session()) {
1740 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1742 _editor->begin_reversible_command (_("Move Video"));
1744 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1745 ARDOUR_UI::instance()->video_timeline->save_undo();
1746 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1747 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1749 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1750 i->view->drag_end();
1751 i->view->fake_set_opaque (false);
1752 i->view->region()->resume_property_changes ();
1754 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1757 _editor->session()->maybe_update_session_range(
1758 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1759 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1763 _editor->commit_reversible_command ();
1764 _editor->update_canvas_now ();
1768 VideoTimeLineDrag::aborted (bool)
1770 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1773 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1774 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1776 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1777 i->view->region()->resume_property_changes ();
1778 i->view->region()->set_position(i->initial_position);
1783 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1784 : RegionDrag (e, i, p, v)
1786 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1787 _preserve_fade_anchor = preserve_fade_anchor;
1791 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1794 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1795 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1797 if (tv && tv->is_track()) {
1798 speed = tv->track()->speed();
1801 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1802 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1803 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1805 framepos_t const pf = adjusted_current_frame (event);
1807 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1808 /* Move the contents of the region around without changing the region bounds */
1809 _operation = ContentsTrim;
1810 Drag::start_grab (event, _editor->cursors()->trimmer);
1812 /* These will get overridden for a point trim.*/
1813 if (pf < (region_start + region_length/2)) {
1814 /* closer to front */
1815 _operation = StartTrim;
1816 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1819 _operation = EndTrim;
1820 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1824 switch (_operation) {
1826 show_verbose_cursor_time (region_start);
1827 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1828 i->view->trim_front_starting ();
1832 show_verbose_cursor_time (region_end);
1835 show_verbose_cursor_time (pf);
1839 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1840 i->view->region()->suspend_property_changes ();
1845 TrimDrag::motion (GdkEvent* event, bool first_move)
1847 RegionView* rv = _primary;
1850 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1851 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1852 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1854 if (tv && tv->is_track()) {
1855 speed = tv->track()->speed();
1858 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1864 switch (_operation) {
1866 trim_type = "Region start trim";
1869 trim_type = "Region end trim";
1872 trim_type = "Region content trim";
1876 _editor->begin_reversible_command (trim_type);
1878 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1879 RegionView* rv = i->view;
1880 rv->fake_set_opaque (false);
1881 rv->enable_display (false);
1882 rv->region()->playlist()->clear_owned_changes ();
1884 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1887 arv->temporarily_hide_envelope ();
1891 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1892 insert_result = _editor->motion_frozen_playlists.insert (pl);
1894 if (insert_result.second) {
1900 bool non_overlap_trim = false;
1902 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1903 non_overlap_trim = true;
1906 switch (_operation) {
1908 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1909 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1910 if (changed && _preserve_fade_anchor) {
1911 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1916 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1917 distance = _drags->current_pointer_x() - grab_x();
1918 len = ar->fade_in()->back()->when;
1919 new_length = len - _editor->unit_to_frame (distance);
1920 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
1921 arv->reset_fade_in_shape_width (ar, new_length); //the grey shape
1928 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1929 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1930 if (changed && _preserve_fade_anchor) {
1931 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1936 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1937 distance = grab_x() - _drags->current_pointer_x();
1938 len = ar->fade_out()->back()->when;
1939 new_length = len - _editor->unit_to_frame (distance);
1940 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
1941 arv->reset_fade_out_shape_width (ar, new_length); //the grey shape
1949 bool swap_direction = false;
1951 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1952 swap_direction = true;
1955 framecnt_t frame_delta = 0;
1957 bool left_direction = false;
1958 if (last_pointer_frame() > adjusted_current_frame(event)) {
1959 left_direction = true;
1962 if (left_direction) {
1963 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1965 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1968 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1969 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1975 switch (_operation) {
1977 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1980 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1983 show_verbose_cursor_time (adjusted_current_frame (event));
1990 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1992 if (movement_occurred) {
1993 motion (event, false);
1995 if (_operation == StartTrim) {
1996 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1998 /* This must happen before the region's StatefulDiffCommand is created, as it may
1999 `correct' (ahem) the region's _start from being negative to being zero. It
2000 needs to be zero in the undo record.
2002 i->view->trim_front_ending ();
2004 if (_preserve_fade_anchor) {
2005 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2010 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2011 distance = _drags->current_pointer_x() - grab_x();
2012 len = ar->fade_in()->back()->when;
2013 new_length = len - _editor->unit_to_frame (distance);
2014 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2015 ar->set_fade_in_length(new_length);
2019 } else if (_operation == EndTrim) {
2020 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2021 if (_preserve_fade_anchor) {
2022 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2027 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2028 distance = _drags->current_pointer_x() - grab_x();
2029 len = ar->fade_out()->back()->when;
2030 new_length = len - _editor->unit_to_frame (distance);
2031 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2032 ar->set_fade_out_length(new_length);
2038 if (!_views.empty()) {
2039 if (_operation == StartTrim) {
2040 _editor->maybe_locate_with_edit_preroll(
2041 _views.begin()->view->region()->position());
2043 if (_operation == EndTrim) {
2044 _editor->maybe_locate_with_edit_preroll(
2045 _views.begin()->view->region()->position() +
2046 _views.begin()->view->region()->length());
2050 if (!_editor->selection->selected (_primary)) {
2051 _primary->thaw_after_trim ();
2054 set<boost::shared_ptr<Playlist> > diffed_playlists;
2056 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2057 i->view->thaw_after_trim ();
2058 i->view->enable_display (true);
2059 i->view->fake_set_opaque (true);
2061 /* Trimming one region may affect others on the playlist, so we need
2062 to get undo Commands from the whole playlist rather than just the
2063 region. Use diffed_playlists to make sure we don't diff a given
2064 playlist more than once.
2066 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2067 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2068 vector<Command*> cmds;
2070 _editor->session()->add_commands (cmds);
2071 diffed_playlists.insert (p);
2076 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2080 _editor->motion_frozen_playlists.clear ();
2081 _editor->commit_reversible_command();
2084 /* no mouse movement */
2085 _editor->point_trim (event, adjusted_current_frame (event));
2088 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2089 if (_operation == StartTrim) {
2090 i->view->trim_front_ending ();
2093 i->view->region()->resume_property_changes ();
2098 TrimDrag::aborted (bool movement_occurred)
2100 /* Our motion method is changing model state, so use the Undo system
2101 to cancel. Perhaps not ideal, as this will leave an Undo point
2102 behind which may be slightly odd from the user's point of view.
2107 if (movement_occurred) {
2111 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2112 i->view->region()->resume_property_changes ();
2117 TrimDrag::setup_pointer_frame_offset ()
2119 list<DraggingView>::iterator i = _views.begin ();
2120 while (i != _views.end() && i->view != _primary) {
2124 if (i == _views.end()) {
2128 switch (_operation) {
2130 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2133 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2140 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2144 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2145 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2150 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2152 Drag::start_grab (event, cursor);
2153 show_verbose_cursor_time (adjusted_current_frame(event));
2157 MeterMarkerDrag::setup_pointer_frame_offset ()
2159 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2163 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2167 // create a dummy marker for visual representation of moving the
2168 // section, because whether its a copy or not, we're going to
2169 // leave or lose the original marker (leave if its a copy; lose if its
2170 // not, because we'll remove it from the map).
2172 MeterSection section (_marker->meter());
2174 if (!section.movable()) {
2179 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2181 _marker = new MeterMarker (
2183 *_editor->meter_group,
2184 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
2186 *new MeterSection (_marker->meter())
2189 /* use the new marker for the grab */
2190 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2193 TempoMap& map (_editor->session()->tempo_map());
2194 /* get current state */
2195 before_state = &map.get_state();
2196 /* remove the section while we drag it */
2197 map.remove_meter (section, true);
2201 framepos_t const pf = adjusted_current_frame (event);
2202 _marker->set_position (pf);
2203 show_verbose_cursor_time (pf);
2207 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2209 if (!movement_occurred) {
2213 motion (event, false);
2215 Timecode::BBT_Time when;
2217 TempoMap& map (_editor->session()->tempo_map());
2218 map.bbt_time (last_pointer_frame(), when);
2220 if (_copy == true) {
2221 _editor->begin_reversible_command (_("copy meter mark"));
2222 XMLNode &before = map.get_state();
2223 map.add_meter (_marker->meter(), when);
2224 XMLNode &after = map.get_state();
2225 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2226 _editor->commit_reversible_command ();
2229 _editor->begin_reversible_command (_("move meter mark"));
2231 /* we removed it before, so add it back now */
2233 map.add_meter (_marker->meter(), when);
2234 XMLNode &after = map.get_state();
2235 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2236 _editor->commit_reversible_command ();
2239 // delete the dummy marker we used for visual representation while moving.
2240 // a new visual marker will show up automatically.
2245 MeterMarkerDrag::aborted (bool moved)
2247 _marker->set_position (_marker->meter().frame ());
2250 TempoMap& map (_editor->session()->tempo_map());
2251 /* we removed it before, so add it back now */
2252 map.add_meter (_marker->meter(), _marker->meter().frame());
2253 // delete the dummy marker we used for visual representation while moving.
2254 // a new visual marker will show up automatically.
2259 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2263 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2265 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2270 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2272 Drag::start_grab (event, cursor);
2273 show_verbose_cursor_time (adjusted_current_frame (event));
2277 TempoMarkerDrag::setup_pointer_frame_offset ()
2279 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2283 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2287 // create a dummy marker for visual representation of moving the
2288 // section, because whether its a copy or not, we're going to
2289 // leave or lose the original marker (leave if its a copy; lose if its
2290 // not, because we'll remove it from the map).
2292 // create a dummy marker for visual representation of moving the copy.
2293 // The actual copying is not done before we reach the finish callback.
2296 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2298 TempoSection section (_marker->tempo());
2300 _marker = new TempoMarker (
2302 *_editor->tempo_group,
2303 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
2305 *new TempoSection (_marker->tempo())
2308 /* use the new marker for the grab */
2309 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2312 TempoMap& map (_editor->session()->tempo_map());
2313 /* get current state */
2314 before_state = &map.get_state();
2315 /* remove the section while we drag it */
2316 map.remove_tempo (section, true);
2320 framepos_t const pf = adjusted_current_frame (event);
2321 _marker->set_position (pf);
2322 show_verbose_cursor_time (pf);
2326 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2328 if (!movement_occurred) {
2332 motion (event, false);
2334 TempoMap& map (_editor->session()->tempo_map());
2335 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2336 Timecode::BBT_Time when;
2338 map.bbt_time (beat_time, when);
2340 if (_copy == true) {
2341 _editor->begin_reversible_command (_("copy tempo mark"));
2342 XMLNode &before = map.get_state();
2343 map.add_tempo (_marker->tempo(), when);
2344 XMLNode &after = map.get_state();
2345 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2346 _editor->commit_reversible_command ();
2349 _editor->begin_reversible_command (_("move tempo mark"));
2350 /* we removed it before, so add it back now */
2351 map.add_tempo (_marker->tempo(), when);
2352 XMLNode &after = map.get_state();
2353 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2354 _editor->commit_reversible_command ();
2357 // delete the dummy marker we used for visual representation while moving.
2358 // a new visual marker will show up automatically.
2363 TempoMarkerDrag::aborted (bool moved)
2365 _marker->set_position (_marker->tempo().frame());
2367 TempoMap& map (_editor->session()->tempo_map());
2368 /* we removed it before, so add it back now */
2369 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2370 // delete the dummy marker we used for visual representation while moving.
2371 // a new visual marker will show up automatically.
2376 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2380 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2383 /** Do all the things we do when dragging the playhead to make it look as though
2384 * we have located, without actually doing the locate (because that would cause
2385 * the diskstream buffers to be refilled, which is too slow).
2388 CursorDrag::fake_locate (framepos_t t)
2390 _editor->playhead_cursor->set_position (t);
2392 Session* s = _editor->session ();
2393 if (s->timecode_transmission_suspended ()) {
2394 framepos_t const f = _editor->playhead_cursor->current_frame ();
2395 s->send_mmc_locate (f);
2396 s->send_full_time_code (f);
2399 show_verbose_cursor_time (t);
2400 _editor->UpdateAllTransportClocks (t);
2404 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2406 Drag::start_grab (event, c);
2408 _grab_zoom = _editor->frames_per_pixel;
2410 framepos_t where = _editor->event_frame (event, 0, 0);
2411 _editor->snap_to_with_modifier (where, event);
2413 _editor->_dragging_playhead = true;
2415 Session* s = _editor->session ();
2418 if (_was_rolling && _stop) {
2422 if (s->is_auditioning()) {
2423 s->cancel_audition ();
2427 if (AudioEngine::instance()->connected()) {
2429 /* do this only if we're the engine is connected
2430 * because otherwise this request will never be
2431 * serviced and we'll busy wait forever. likewise,
2432 * notice if we are disconnected while waiting for the
2433 * request to be serviced.
2436 s->request_suspend_timecode_transmission ();
2437 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2438 /* twiddle our thumbs */
2443 fake_locate (where);
2447 CursorDrag::motion (GdkEvent* event, bool)
2449 framepos_t const adjusted_frame = adjusted_current_frame (event);
2450 if (adjusted_frame != last_pointer_frame()) {
2451 fake_locate (adjusted_frame);
2453 _editor->update_canvas_now ();
2459 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2461 _editor->_dragging_playhead = false;
2463 if (!movement_occurred && _stop) {
2467 motion (event, false);
2469 Session* s = _editor->session ();
2471 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2472 _editor->_pending_locate_request = true;
2473 s->request_resume_timecode_transmission ();
2478 CursorDrag::aborted (bool)
2480 if (_editor->_dragging_playhead) {
2481 _editor->session()->request_resume_timecode_transmission ();
2482 _editor->_dragging_playhead = false;
2485 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2488 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2489 : RegionDrag (e, i, p, v)
2491 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2495 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2497 Drag::start_grab (event, cursor);
2499 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2500 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2502 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2506 FadeInDrag::setup_pointer_frame_offset ()
2508 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2509 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2510 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2514 FadeInDrag::motion (GdkEvent* event, bool)
2516 framecnt_t fade_length;
2518 framepos_t const pos = adjusted_current_frame (event);
2520 boost::shared_ptr<Region> region = _primary->region ();
2522 if (pos < (region->position() + 64)) {
2523 fade_length = 64; // this should be a minimum defined somewhere
2524 } else if (pos > region->last_frame()) {
2525 fade_length = region->length();
2527 fade_length = pos - region->position();
2530 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2532 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2538 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2541 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2545 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2547 if (!movement_occurred) {
2551 framecnt_t fade_length;
2553 framepos_t const pos = adjusted_current_frame (event);
2555 boost::shared_ptr<Region> region = _primary->region ();
2557 if (pos < (region->position() + 64)) {
2558 fade_length = 64; // this should be a minimum defined somewhere
2559 } else if (pos > region->last_frame()) {
2560 fade_length = region->length();
2562 fade_length = pos - region->position();
2565 _editor->begin_reversible_command (_("change fade in length"));
2567 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2569 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2575 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2576 XMLNode &before = alist->get_state();
2578 tmp->audio_region()->set_fade_in_length (fade_length);
2579 tmp->audio_region()->set_fade_in_active (true);
2581 XMLNode &after = alist->get_state();
2582 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2585 _editor->commit_reversible_command ();
2589 FadeInDrag::aborted (bool)
2591 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2592 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2598 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2602 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2603 : RegionDrag (e, i, p, v)
2605 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2609 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2611 Drag::start_grab (event, cursor);
2613 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2614 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2616 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2620 FadeOutDrag::setup_pointer_frame_offset ()
2622 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2623 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2624 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2628 FadeOutDrag::motion (GdkEvent* event, bool)
2630 framecnt_t fade_length;
2632 framepos_t const pos = adjusted_current_frame (event);
2634 boost::shared_ptr<Region> region = _primary->region ();
2636 if (pos > (region->last_frame() - 64)) {
2637 fade_length = 64; // this should really be a minimum fade defined somewhere
2639 else if (pos < region->position()) {
2640 fade_length = region->length();
2643 fade_length = region->last_frame() - pos;
2646 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2648 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2654 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2657 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2661 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2663 if (!movement_occurred) {
2667 framecnt_t fade_length;
2669 framepos_t const pos = adjusted_current_frame (event);
2671 boost::shared_ptr<Region> region = _primary->region ();
2673 if (pos > (region->last_frame() - 64)) {
2674 fade_length = 64; // this should really be a minimum fade defined somewhere
2676 else if (pos < region->position()) {
2677 fade_length = region->length();
2680 fade_length = region->last_frame() - pos;
2683 _editor->begin_reversible_command (_("change fade out length"));
2685 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2687 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2693 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2694 XMLNode &before = alist->get_state();
2696 tmp->audio_region()->set_fade_out_length (fade_length);
2697 tmp->audio_region()->set_fade_out_active (true);
2699 XMLNode &after = alist->get_state();
2700 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2703 _editor->commit_reversible_command ();
2707 FadeOutDrag::aborted (bool)
2709 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2710 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2716 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2720 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2723 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2725 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2728 _points.push_back (ArdourCanvas::Duple (0, 0));
2729 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2732 MarkerDrag::~MarkerDrag ()
2734 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2739 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2741 location = new Location (*l);
2742 markers.push_back (m);
2747 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2749 Drag::start_grab (event, cursor);
2753 Location *location = _editor->find_location_from_marker (_marker, is_start);
2754 _editor->_dragging_edit_point = true;
2756 update_item (location);
2758 // _drag_line->show();
2759 // _line->raise_to_top();
2762 show_verbose_cursor_time (location->start());
2764 show_verbose_cursor_time (location->end());
2767 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2770 case Selection::Toggle:
2771 /* we toggle on the button release */
2773 case Selection::Set:
2774 if (!_editor->selection->selected (_marker)) {
2775 _editor->selection->set (_marker);
2778 case Selection::Extend:
2780 Locations::LocationList ll;
2781 list<Marker*> to_add;
2783 _editor->selection->markers.range (s, e);
2784 s = min (_marker->position(), s);
2785 e = max (_marker->position(), e);
2788 if (e < max_framepos) {
2791 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2792 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2793 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2796 to_add.push_back (lm->start);
2799 to_add.push_back (lm->end);
2803 if (!to_add.empty()) {
2804 _editor->selection->add (to_add);
2808 case Selection::Add:
2809 _editor->selection->add (_marker);
2813 /* Set up copies for us to manipulate during the drag
2816 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2818 Location* l = _editor->find_location_from_marker (*i, is_start);
2825 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2827 /* range: check that the other end of the range isn't
2830 CopiedLocationInfo::iterator x;
2831 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2832 if (*(*x).location == *l) {
2836 if (x == _copied_locations.end()) {
2837 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2839 (*x).markers.push_back (*i);
2840 (*x).move_both = true;
2848 MarkerDrag::setup_pointer_frame_offset ()
2851 Location *location = _editor->find_location_from_marker (_marker, is_start);
2852 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2856 MarkerDrag::motion (GdkEvent* event, bool)
2858 framecnt_t f_delta = 0;
2860 bool move_both = false;
2861 Location *real_location;
2862 Location *copy_location = 0;
2864 framepos_t const newframe = adjusted_current_frame (event);
2865 framepos_t next = newframe;
2867 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2871 CopiedLocationInfo::iterator x;
2873 /* find the marker we're dragging, and compute the delta */
2875 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2877 copy_location = (*x).location;
2879 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2881 /* this marker is represented by this
2882 * CopiedLocationMarkerInfo
2885 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
2890 if (real_location->is_mark()) {
2891 f_delta = newframe - copy_location->start();
2895 switch (_marker->type()) {
2896 case Marker::SessionStart:
2897 case Marker::RangeStart:
2898 case Marker::LoopStart:
2899 case Marker::PunchIn:
2900 f_delta = newframe - copy_location->start();
2903 case Marker::SessionEnd:
2904 case Marker::RangeEnd:
2905 case Marker::LoopEnd:
2906 case Marker::PunchOut:
2907 f_delta = newframe - copy_location->end();
2910 /* what kind of marker is this ? */
2919 if (x == _copied_locations.end()) {
2920 /* hmm, impossible - we didn't find the dragged marker */
2924 /* now move them all */
2926 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2928 copy_location = x->location;
2930 /* call this to find out if its the start or end */
2932 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
2936 if (real_location->locked()) {
2940 if (copy_location->is_mark()) {
2944 copy_location->set_start (copy_location->start() + f_delta);
2948 framepos_t new_start = copy_location->start() + f_delta;
2949 framepos_t new_end = copy_location->end() + f_delta;
2951 if (is_start) { // start-of-range marker
2953 if (move_both || (*x).move_both) {
2954 copy_location->set_start (new_start);
2955 copy_location->set_end (new_end);
2956 } else if (new_start < copy_location->end()) {
2957 copy_location->set_start (new_start);
2958 } else if (newframe > 0) {
2959 _editor->snap_to (next, 1, true);
2960 copy_location->set_end (next);
2961 copy_location->set_start (newframe);
2964 } else { // end marker
2966 if (move_both || (*x).move_both) {
2967 copy_location->set_end (new_end);
2968 copy_location->set_start (new_start);
2969 } else if (new_end > copy_location->start()) {
2970 copy_location->set_end (new_end);
2971 } else if (newframe > 0) {
2972 _editor->snap_to (next, -1, true);
2973 copy_location->set_start (next);
2974 copy_location->set_end (newframe);
2979 update_item (copy_location);
2981 /* now lookup the actual GUI items used to display this
2982 * location and move them to wherever the copy of the location
2983 * is now. This means that the logic in ARDOUR::Location is
2984 * still enforced, even though we are not (yet) modifying
2985 * the real Location itself.
2988 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2991 lm->set_position (copy_location->start(), copy_location->end());
2996 assert (!_copied_locations.empty());
2998 show_verbose_cursor_time (newframe);
3001 _editor->update_canvas_now ();
3006 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3008 if (!movement_occurred) {
3010 /* just a click, do nothing but finish
3011 off the selection process
3014 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3017 case Selection::Set:
3018 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3019 _editor->selection->set (_marker);
3023 case Selection::Toggle:
3024 /* we toggle on the button release, click only */
3025 _editor->selection->toggle (_marker);
3028 case Selection::Extend:
3029 case Selection::Add:
3036 _editor->_dragging_edit_point = false;
3038 _editor->begin_reversible_command ( _("move marker") );
3039 XMLNode &before = _editor->session()->locations()->get_state();
3041 MarkerSelection::iterator i;
3042 CopiedLocationInfo::iterator x;
3045 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3046 x != _copied_locations.end() && i != _editor->selection->markers.end();
3049 Location * location = _editor->find_location_from_marker (*i, is_start);
3053 if (location->locked()) {
3057 if (location->is_mark()) {
3058 location->set_start (((*x).location)->start());
3060 location->set (((*x).location)->start(), ((*x).location)->end());
3065 XMLNode &after = _editor->session()->locations()->get_state();
3066 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3067 _editor->commit_reversible_command ();
3071 MarkerDrag::aborted (bool)
3077 MarkerDrag::update_item (Location*)
3082 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3084 _cumulative_x_drag (0),
3085 _cumulative_y_drag (0)
3087 if (_zero_gain_fraction < 0.0) {
3088 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3091 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3093 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3099 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3101 Drag::start_grab (event, _editor->cursors()->fader);
3103 // start the grab at the center of the control point so
3104 // the point doesn't 'jump' to the mouse after the first drag
3105 _fixed_grab_x = _point->get_x();
3106 _fixed_grab_y = _point->get_y();
3108 float const fraction = 1 - (_point->get_y() / _point->line().height());
3110 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3112 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3113 event->button.x + 10, event->button.y + 10);
3115 _editor->verbose_cursor()->show ();
3117 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3119 if (!_point->can_slide ()) {
3120 _x_constrained = true;
3125 ControlPointDrag::motion (GdkEvent* event, bool)
3127 double dx = _drags->current_pointer_x() - last_pointer_x();
3128 double dy = _drags->current_pointer_y() - last_pointer_y();
3130 if (event->button.state & Keyboard::SecondaryModifier) {
3135 /* coordinate in pixels relative to the start of the region (for region-based automation)
3136 or track (for track-based automation) */
3137 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3138 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3140 // calculate zero crossing point. back off by .01 to stay on the
3141 // positive side of zero
3142 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3144 // make sure we hit zero when passing through
3145 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3149 if (_x_constrained) {
3152 if (_y_constrained) {
3156 _cumulative_x_drag = cx - _fixed_grab_x;
3157 _cumulative_y_drag = cy - _fixed_grab_y;
3161 cy = min ((double) _point->line().height(), cy);
3163 framepos_t cx_frames = _editor->unit_to_frame (cx);
3165 if (!_x_constrained) {
3166 _editor->snap_to_with_modifier (cx_frames, event);
3169 cx_frames = min (cx_frames, _point->line().maximum_time());
3171 float const fraction = 1.0 - (cy / _point->line().height());
3173 _point->line().drag_motion (_editor->frame_to_unit_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3175 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3179 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3181 if (!movement_occurred) {
3185 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3186 _editor->reset_point_selection ();
3190 motion (event, false);
3193 _point->line().end_drag (_pushing, _final_index);
3194 _editor->session()->commit_reversible_command ();
3198 ControlPointDrag::aborted (bool)
3200 _point->line().reset ();
3204 ControlPointDrag::active (Editing::MouseMode m)
3206 if (m == Editing::MouseGain) {
3207 /* always active in mouse gain */
3211 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3212 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3215 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3218 _cumulative_y_drag (0)
3220 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3224 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3226 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3229 _item = &_line->grab_item ();
3231 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3232 origin, and ditto for y.
3235 double cx = event->button.x;
3236 double cy = event->button.y;
3238 _line->parent_group().canvas_to_item (cx, cy);
3240 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_pixel);
3245 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3246 /* no adjacent points */
3250 Drag::start_grab (event, _editor->cursors()->fader);
3252 /* store grab start in parent frame */
3257 double fraction = 1.0 - (cy / _line->height());
3259 _line->start_drag_line (before, after, fraction);
3261 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3262 event->button.x + 10, event->button.y + 10);
3264 _editor->verbose_cursor()->show ();
3268 LineDrag::motion (GdkEvent* event, bool)
3270 double dy = _drags->current_pointer_y() - last_pointer_y();
3272 if (event->button.state & Keyboard::SecondaryModifier) {
3276 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3278 _cumulative_y_drag = cy - _fixed_grab_y;
3281 cy = min ((double) _line->height(), cy);
3283 double const fraction = 1.0 - (cy / _line->height());
3286 /* we are ignoring x position for this drag, so we can just pass in anything */
3287 _line->drag_motion (0, fraction, true, false, ignored);
3289 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3293 LineDrag::finished (GdkEvent* event, bool)
3295 motion (event, false);
3296 _line->end_drag (false, 0);
3297 _editor->session()->commit_reversible_command ();
3301 LineDrag::aborted (bool)
3306 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3309 _cumulative_x_drag (0)
3311 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3315 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3317 Drag::start_grab (event);
3319 _line = reinterpret_cast<Line*> (_item);
3322 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3324 double cx = event->button.x;
3325 double cy = event->button.y;
3327 _item->parent()->canvas_to_item (cx, cy);
3329 /* store grab start in parent frame */
3330 _region_view_grab_x = cx;
3332 _before = *(float*) _item->get_data ("position");
3334 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3336 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3340 FeatureLineDrag::motion (GdkEvent*, bool)
3342 double dx = _drags->current_pointer_x() - last_pointer_x();
3344 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3346 _cumulative_x_drag += dx;
3348 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3357 boost::optional<Rect> bbox = _line->bounding_box ();
3359 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3361 float *pos = new float;
3364 _line->set_data ("position", pos);
3370 FeatureLineDrag::finished (GdkEvent*, bool)
3372 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3373 _arv->update_transient(_before, _before);
3377 FeatureLineDrag::aborted (bool)
3382 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3384 , _vertical_only (false)
3386 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3390 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3392 Drag::start_grab (event);
3393 show_verbose_cursor_time (adjusted_current_frame (event));
3397 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3404 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3406 framepos_t grab = grab_frame ();
3407 if (Config->get_rubberbanding_snaps_to_grid ()) {
3408 _editor->snap_to_with_modifier (grab, event);
3411 /* base start and end on initial click position */
3421 if (_drags->current_pointer_y() < grab_y()) {
3422 y1 = _drags->current_pointer_y();
3425 y2 = _drags->current_pointer_y();
3430 if (start != end || y1 != y2) {
3432 double x1 = _editor->frame_to_pixel (start);
3433 double x2 = _editor->frame_to_pixel (end);
3435 _editor->rubberband_rect->set_x0 (x1);
3436 if (_vertical_only) {
3437 /* fixed 10 pixel width */
3438 _editor->rubberband_rect->set_x1 (x1 + 10);
3440 _editor->rubberband_rect->set_x1 (x2);
3443 _editor->rubberband_rect->set_y0 (y1);
3444 _editor->rubberband_rect->set_y1 (y2);
3446 _editor->rubberband_rect->show();
3447 _editor->rubberband_rect->raise_to_top();
3449 show_verbose_cursor_time (pf);
3451 do_select_things (event, true);
3456 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3461 if (grab_frame() < last_pointer_frame()) {
3463 x2 = last_pointer_frame ();
3466 x1 = last_pointer_frame ();
3472 if (_drags->current_pointer_y() < grab_y()) {
3473 y1 = _drags->current_pointer_y();
3476 y2 = _drags->current_pointer_y();
3480 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3484 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3486 if (movement_occurred) {
3488 motion (event, false);
3489 do_select_things (event, false);
3495 bool do_deselect = true;
3496 MidiTimeAxisView* mtv;
3498 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3500 if (_editor->selection->empty()) {
3501 /* nothing selected */
3502 add_midi_region (mtv);
3503 do_deselect = false;
3507 /* do not deselect if Primary or Tertiary (toggle-select or
3508 * extend-select are pressed.
3511 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3512 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3519 _editor->rubberband_rect->hide();
3523 RubberbandSelectDrag::aborted (bool)
3525 _editor->rubberband_rect->hide ();
3528 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3529 : RegionDrag (e, i, p, v)
3531 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3535 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3537 Drag::start_grab (event, cursor);
3539 show_verbose_cursor_time (adjusted_current_frame (event));
3543 TimeFXDrag::motion (GdkEvent* event, bool)
3545 RegionView* rv = _primary;
3546 StreamView* cv = rv->get_time_axis_view().view ();
3548 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3549 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3550 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3552 framepos_t const pf = adjusted_current_frame (event);
3554 if (pf > rv->region()->position()) {
3555 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3558 show_verbose_cursor_time (pf);
3562 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3564 _primary->get_time_axis_view().hide_timestretch ();
3566 if (!movement_occurred) {
3570 if (last_pointer_frame() < _primary->region()->position()) {
3571 /* backwards drag of the left edge - not usable */
3575 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3577 float percentage = (double) newlen / (double) _primary->region()->length();
3579 #ifndef USE_RUBBERBAND
3580 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3581 if (_primary->region()->data_type() == DataType::AUDIO) {
3582 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3586 if (!_editor->get_selection().regions.empty()) {
3587 /* primary will already be included in the selection, and edit
3588 group shared editing will propagate selection across
3589 equivalent regions, so just use the current region
3593 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3594 error << _("An error occurred while executing time stretch operation") << endmsg;
3600 TimeFXDrag::aborted (bool)
3602 _primary->get_time_axis_view().hide_timestretch ();
3605 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3608 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3612 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3614 Drag::start_grab (event);
3618 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3620 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3624 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3626 if (movement_occurred && _editor->session()) {
3627 /* make sure we stop */
3628 _editor->session()->request_transport_speed (0.0);
3633 ScrubDrag::aborted (bool)
3638 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3643 , _original_pointer_time_axis (-1)
3644 , _last_pointer_time_axis (-1)
3645 , _time_selection_at_start (!_editor->get_selection().time.empty())
3647 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3649 if (_time_selection_at_start) {
3650 start_at_start = _editor->get_selection().time.start();
3651 end_at_start = _editor->get_selection().time.end_frame();
3656 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3658 if (_editor->session() == 0) {
3662 Gdk::Cursor* cursor = 0;
3664 switch (_operation) {
3665 case CreateSelection:
3666 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3671 cursor = _editor->cursors()->selector;
3672 Drag::start_grab (event, cursor);
3675 case SelectionStartTrim:
3676 if (_editor->clicked_axisview) {
3677 _editor->clicked_axisview->order_selection_trims (_item, true);
3679 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3682 case SelectionEndTrim:
3683 if (_editor->clicked_axisview) {
3684 _editor->clicked_axisview->order_selection_trims (_item, false);
3686 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3690 Drag::start_grab (event, cursor);
3693 case SelectionExtend:
3694 Drag::start_grab (event, cursor);
3698 if (_operation == SelectionMove) {
3699 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3701 show_verbose_cursor_time (adjusted_current_frame (event));
3704 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3708 SelectionDrag::setup_pointer_frame_offset ()
3710 switch (_operation) {
3711 case CreateSelection:
3712 _pointer_frame_offset = 0;
3715 case SelectionStartTrim:
3717 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3720 case SelectionEndTrim:
3721 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3724 case SelectionExtend:
3730 SelectionDrag::motion (GdkEvent* event, bool first_move)
3732 framepos_t start = 0;
3734 framecnt_t length = 0;
3735 framecnt_t distance = 0;
3737 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3738 if (pending_time_axis.first == 0) {
3742 framepos_t const pending_position = adjusted_current_frame (event);
3744 /* only alter selection if things have changed */
3746 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3750 switch (_operation) {
3751 case CreateSelection:
3753 framepos_t grab = grab_frame ();
3756 grab = adjusted_current_frame (event, false);
3757 if (grab < pending_position) {
3758 _editor->snap_to (grab, -1);
3760 _editor->snap_to (grab, 1);
3764 if (pending_position < grab) {
3765 start = pending_position;
3768 end = pending_position;
3772 /* first drag: Either add to the selection
3773 or create a new selection
3779 /* adding to the selection */
3780 _editor->set_selected_track_as_side_effect (Selection::Add);
3781 //_editor->selection->add (_editor->clicked_axisview);
3782 _editor->clicked_selection = _editor->selection->add (start, end);
3787 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3788 //_editor->selection->set (_editor->clicked_axisview);
3789 _editor->set_selected_track_as_side_effect (Selection::Set);
3792 _editor->clicked_selection = _editor->selection->set (start, end);
3796 /* select the track that we're in */
3797 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3798 // _editor->set_selected_track_as_side_effect (Selection::Add);
3799 _editor->selection->add (pending_time_axis.first);
3800 _added_time_axes.push_back (pending_time_axis.first);
3803 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3804 tracks that we selected in the first place.
3807 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3808 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3810 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3811 while (i != _added_time_axes.end()) {
3813 list<TimeAxisView*>::iterator tmp = i;
3816 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3817 _editor->selection->remove (*i);
3818 _added_time_axes.remove (*i);
3827 case SelectionStartTrim:
3829 start = _editor->selection->time[_editor->clicked_selection].start;
3830 end = _editor->selection->time[_editor->clicked_selection].end;
3832 if (pending_position > end) {
3835 start = pending_position;
3839 case SelectionEndTrim:
3841 start = _editor->selection->time[_editor->clicked_selection].start;
3842 end = _editor->selection->time[_editor->clicked_selection].end;
3844 if (pending_position < start) {
3847 end = pending_position;
3854 start = _editor->selection->time[_editor->clicked_selection].start;
3855 end = _editor->selection->time[_editor->clicked_selection].end;
3857 length = end - start;
3858 distance = pending_position - start;
3859 start = pending_position;
3860 _editor->snap_to (start);
3862 end = start + length;
3866 case SelectionExtend:
3870 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
3871 _editor->start_canvas_autoscroll (1, 0);
3875 switch (_operation) {
3877 if (_time_selection_at_start) {
3878 _editor->selection->move_time (distance);
3882 _editor->selection->replace (_editor->clicked_selection, start, end);
3886 if (_operation == SelectionMove) {
3887 show_verbose_cursor_time(start);
3889 show_verbose_cursor_time(pending_position);
3894 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3896 Session* s = _editor->session();
3898 if (movement_occurred) {
3899 motion (event, false);
3900 /* XXX this is not object-oriented programming at all. ick */
3901 if (_editor->selection->time.consolidate()) {
3902 _editor->selection->TimeChanged ();
3905 /* XXX what if its a music time selection? */
3907 if ( s->get_play_range() && s->transport_rolling() ) {
3908 s->request_play_range (&_editor->selection->time, true);
3910 if (Config->get_always_play_range() && !s->transport_rolling()) {
3911 s->request_locate (_editor->get_selection().time.start());
3917 /* just a click, no pointer movement.
3920 if (_operation == SelectionExtend) {
3921 if (_time_selection_at_start) {
3922 framepos_t pos = adjusted_current_frame (event, false);
3923 framepos_t start = min (pos, start_at_start);
3924 framepos_t end = max (pos, end_at_start);
3925 _editor->selection->set (start, end);
3928 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3929 if (_editor->clicked_selection) {
3930 _editor->selection->remove (_editor->clicked_selection);
3933 if (!_editor->clicked_selection) {
3934 _editor->selection->clear_time();
3939 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3940 _editor->selection->set (_editor->clicked_axisview);
3943 if (s && s->get_play_range () && s->transport_rolling()) {
3944 s->request_stop (false, false);
3949 _editor->stop_canvas_autoscroll ();
3950 _editor->clicked_selection = 0;
3954 SelectionDrag::aborted (bool)
3959 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3964 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3966 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
3967 ArdourCanvas::Rect (0.0, 0.0, 0.0,
3968 physical_screen_height (_editor->get_window())));
3969 _drag_rect->hide ();
3971 _drag_rect->set_fill_color (ARDOUR_UI::config()->canvasvar_RangeDragRect.get());
3972 _drag_rect->set_outline_color (ARDOUR_UI::config()->canvasvar_RangeDragRect.get());
3976 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3978 if (_editor->session() == 0) {
3982 Gdk::Cursor* cursor = 0;
3984 if (!_editor->temp_location) {
3985 _editor->temp_location = new Location (*_editor->session());
3988 switch (_operation) {
3989 case CreateRangeMarker:
3990 case CreateTransportMarker:
3991 case CreateCDMarker:
3993 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3998 cursor = _editor->cursors()->selector;
4002 Drag::start_grab (event, cursor);
4004 show_verbose_cursor_time (adjusted_current_frame (event));
4008 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4010 framepos_t start = 0;
4012 ArdourCanvas::Rectangle *crect;
4014 switch (_operation) {
4015 case CreateRangeMarker:
4016 crect = _editor->range_bar_drag_rect;
4018 case CreateTransportMarker:
4019 crect = _editor->transport_bar_drag_rect;
4021 case CreateCDMarker:
4022 crect = _editor->cd_marker_bar_drag_rect;
4025 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4030 framepos_t const pf = adjusted_current_frame (event);
4032 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4033 framepos_t grab = grab_frame ();
4034 _editor->snap_to (grab);
4036 if (pf < grab_frame()) {
4044 /* first drag: Either add to the selection
4045 or create a new selection.
4050 _editor->temp_location->set (start, end);
4054 update_item (_editor->temp_location);
4056 //_drag_rect->raise_to_top();
4061 if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
4062 _editor->start_canvas_autoscroll (1, 0);
4066 _editor->temp_location->set (start, end);
4068 double x1 = _editor->frame_to_pixel (start);
4069 double x2 = _editor->frame_to_pixel (end);
4073 update_item (_editor->temp_location);
4076 show_verbose_cursor_time (pf);
4081 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4083 Location * newloc = 0;
4087 if (movement_occurred) {
4088 motion (event, false);
4091 switch (_operation) {
4092 case CreateRangeMarker:
4093 case CreateCDMarker:
4095 _editor->begin_reversible_command (_("new range marker"));
4096 XMLNode &before = _editor->session()->locations()->get_state();
4097 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4098 if (_operation == CreateCDMarker) {
4099 flags = Location::IsRangeMarker | Location::IsCDMarker;
4100 _editor->cd_marker_bar_drag_rect->hide();
4103 flags = Location::IsRangeMarker;
4104 _editor->range_bar_drag_rect->hide();
4106 newloc = new Location (
4107 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4110 _editor->session()->locations()->add (newloc, true);
4111 XMLNode &after = _editor->session()->locations()->get_state();
4112 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4113 _editor->commit_reversible_command ();
4117 case CreateTransportMarker:
4118 // popup menu to pick loop or punch
4119 _editor->new_transport_marker_context_menu (&event->button, _item);
4123 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4125 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
4130 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4132 if (end == max_framepos) {
4133 end = _editor->session()->current_end_frame ();
4136 if (start == max_framepos) {
4137 start = _editor->session()->current_start_frame ();
4140 switch (_editor->mouse_mode) {
4142 /* find the two markers on either side and then make the selection from it */
4143 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4147 /* find the two markers on either side of the click and make the range out of it */
4148 _editor->selection->set (start, end);
4157 _editor->stop_canvas_autoscroll ();
4161 RangeMarkerBarDrag::aborted (bool)
4167 RangeMarkerBarDrag::update_item (Location* location)
4169 double const x1 = _editor->frame_to_pixel (location->start());
4170 double const x2 = _editor->frame_to_pixel (location->end());
4172 _drag_rect->set_x0 (x1);
4173 _drag_rect->set_x1 (x2);
4176 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4180 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4184 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4186 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4187 Drag::start_grab (event, _editor->cursors()->zoom_out);
4190 Drag::start_grab (event, _editor->cursors()->zoom_in);
4194 show_verbose_cursor_time (adjusted_current_frame (event));
4198 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4203 framepos_t const pf = adjusted_current_frame (event);
4205 framepos_t grab = grab_frame ();
4206 _editor->snap_to_with_modifier (grab, event);
4208 /* base start and end on initial click position */
4220 _editor->zoom_rect->show();
4221 _editor->zoom_rect->raise_to_top();
4224 _editor->reposition_zoom_rect(start, end);
4226 show_verbose_cursor_time (pf);
4231 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4233 if (movement_occurred) {
4234 motion (event, false);
4236 if (grab_frame() < last_pointer_frame()) {
4237 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4239 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4242 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4243 _editor->tav_zoom_step (_zoom_out);
4245 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4249 _editor->zoom_rect->hide();
4253 MouseZoomDrag::aborted (bool)
4255 _editor->zoom_rect->hide ();
4258 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4260 , _cumulative_dx (0)
4261 , _cumulative_dy (0)
4263 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4265 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4267 _region = &_primary->region_view ();
4268 _note_height = _region->midi_stream_view()->note_height ();
4272 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4274 Drag::start_grab (event);
4276 if (!(_was_selected = _primary->selected())) {
4278 /* tertiary-click means extend selection - we'll do that on button release,
4279 so don't add it here, because otherwise we make it hard to figure
4280 out the "extend-to" range.
4283 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4286 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4289 _region->note_selected (_primary, true);
4291 _region->unique_select (_primary);
4297 /** @return Current total drag x change in frames */
4299 NoteDrag::total_dx () const
4302 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
4304 /* primary note time */
4305 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4307 /* new time of the primary note in session frames */
4308 frameoffset_t st = n + dx;
4310 framepos_t const rp = _region->region()->position ();
4312 /* prevent the note being dragged earlier than the region's position */
4315 /* snap and return corresponding delta */
4316 return _region->snap_frame_to_frame (st - rp) + rp - n;
4319 /** @return Current total drag y change in note number */
4321 NoteDrag::total_dy () const
4323 MidiStreamView* msv = _region->midi_stream_view ();
4324 double const y = _region->midi_view()->y_position ();
4325 /* new current note */
4326 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4328 n = max (msv->lowest_note(), n);
4329 n = min (msv->highest_note(), n);
4330 /* and work out delta */
4331 return n - msv->y_to_note (grab_y() - y);
4335 NoteDrag::motion (GdkEvent *, bool)
4337 /* Total change in x and y since the start of the drag */
4338 frameoffset_t const dx = total_dx ();
4339 int8_t const dy = total_dy ();
4341 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4342 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
4343 double const tdy = -dy * _note_height - _cumulative_dy;
4346 _cumulative_dx += tdx;
4347 _cumulative_dy += tdy;
4349 int8_t note_delta = total_dy();
4351 _region->move_selection (tdx, tdy, note_delta);
4353 /* the new note value may be the same as the old one, but we
4354 * don't know what that means because the selection may have
4355 * involved more than one note and we might be doing something
4356 * odd with them. so show the note value anyway, always.
4360 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4362 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4363 (int) floor (new_note));
4365 show_verbose_cursor_text (buf);
4370 NoteDrag::finished (GdkEvent* ev, bool moved)
4373 /* no motion - select note */
4375 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4376 _editor->current_mouse_mode() == Editing::MouseDraw) {
4378 if (_was_selected) {
4379 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4381 _region->note_deselected (_primary);
4384 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4385 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4387 if (!extend && !add && _region->selection_size() > 1) {
4388 _region->unique_select (_primary);
4389 } else if (extend) {
4390 _region->note_selected (_primary, true, true);
4392 /* it was added during button press */
4397 _region->note_dropped (_primary, total_dx(), total_dy());
4402 NoteDrag::aborted (bool)
4407 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4408 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4409 : Drag (editor, atv->base_item ())
4411 , _nothing_to_drag (false)
4413 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4414 y_origin = atv->y_position();
4415 setup (atv->lines ());
4418 /** Make an AutomationRangeDrag for region gain lines */
4419 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4420 : Drag (editor, rv->get_canvas_group ())
4422 , _nothing_to_drag (false)
4424 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4426 list<boost::shared_ptr<AutomationLine> > lines;
4427 lines.push_back (rv->get_gain_line ());
4428 y_origin = rv->get_time_axis_view().y_position();
4432 /** @param lines AutomationLines to drag.
4433 * @param offset Offset from the session start to the points in the AutomationLines.
4436 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4438 /* find the lines that overlap the ranges being dragged */
4439 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4440 while (i != lines.end ()) {
4441 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4444 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4446 /* check this range against all the AudioRanges that we are using */
4447 list<AudioRange>::const_iterator k = _ranges.begin ();
4448 while (k != _ranges.end()) {
4449 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4455 /* add it to our list if it overlaps at all */
4456 if (k != _ranges.end()) {
4461 _lines.push_back (n);
4467 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4471 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4473 return 1.0 - ((global_y - y_origin) / line->height());
4477 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4479 Drag::start_grab (event, cursor);
4481 /* Get line states before we start changing things */
4482 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4483 i->state = &i->line->get_state ();
4484 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4487 if (_ranges.empty()) {
4489 /* No selected time ranges: drag all points */
4490 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4491 uint32_t const N = i->line->npoints ();
4492 for (uint32_t j = 0; j < N; ++j) {
4493 i->points.push_back (i->line->nth (j));
4499 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4501 framecnt_t const half = (i->start + i->end) / 2;
4503 /* find the line that this audio range starts in */
4504 list<Line>::iterator j = _lines.begin();
4505 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4509 if (j != _lines.end()) {
4510 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4512 /* j is the line that this audio range starts in; fade into it;
4513 64 samples length plucked out of thin air.
4516 framepos_t a = i->start + 64;
4521 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4522 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4524 the_list->add (p, the_list->eval (p));
4525 the_list->add (q, the_list->eval (q));
4528 /* same thing for the end */
4531 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4535 if (j != _lines.end()) {
4536 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4538 /* j is the line that this audio range starts in; fade out of it;
4539 64 samples length plucked out of thin air.
4542 framepos_t b = i->end - 64;
4547 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4548 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4550 the_list->add (p, the_list->eval (p));
4551 the_list->add (q, the_list->eval (q));
4555 _nothing_to_drag = true;
4557 /* Find all the points that should be dragged and put them in the relevant
4558 points lists in the Line structs.
4561 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4563 uint32_t const N = i->line->npoints ();
4564 for (uint32_t j = 0; j < N; ++j) {
4566 /* here's a control point on this line */
4567 ControlPoint* p = i->line->nth (j);
4568 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4570 /* see if it's inside a range */
4571 list<AudioRange>::const_iterator k = _ranges.begin ();
4572 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4576 if (k != _ranges.end()) {
4577 /* dragging this point */
4578 _nothing_to_drag = false;
4579 i->points.push_back (p);
4585 if (_nothing_to_drag) {
4589 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4590 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4595 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4597 if (_nothing_to_drag) {
4601 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4602 float const f = y_fraction (l->line, _drags->current_pointer_y());
4603 /* we are ignoring x position for this drag, so we can just pass in anything */
4605 l->line->drag_motion (0, f, true, false, ignored);
4606 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4611 AutomationRangeDrag::finished (GdkEvent* event, bool)
4613 if (_nothing_to_drag) {
4617 motion (event, false);
4618 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4619 i->line->end_drag (false, 0);
4622 _editor->session()->commit_reversible_command ();
4626 AutomationRangeDrag::aborted (bool)
4628 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4633 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4636 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4637 layer = v->region()->layer ();
4638 initial_y = v->get_canvas_group()->position().y;
4639 initial_playlist = v->region()->playlist ();
4640 initial_position = v->region()->position ();
4641 initial_end = v->region()->position () + v->region()->length ();
4644 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4645 : Drag (e, i->canvas_item ())
4648 , _cumulative_dx (0)
4650 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4651 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4656 PatchChangeDrag::motion (GdkEvent* ev, bool)
4658 framepos_t f = adjusted_current_frame (ev);
4659 boost::shared_ptr<Region> r = _region_view->region ();
4660 f = max (f, r->position ());
4661 f = min (f, r->last_frame ());
4663 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4664 double const dxu = _editor->frame_to_unit (dxf); // permitted fx in units
4665 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4666 _cumulative_dx = dxu;
4670 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4672 if (!movement_occurred) {
4676 boost::shared_ptr<Region> r (_region_view->region ());
4677 framepos_t f = adjusted_current_frame (ev);
4678 f = max (f, r->position ());
4679 f = min (f, r->last_frame ());
4681 _region_view->move_patch_change (
4683 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4688 PatchChangeDrag::aborted (bool)
4690 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4694 PatchChangeDrag::setup_pointer_frame_offset ()
4696 boost::shared_ptr<Region> region = _region_view->region ();
4697 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4700 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4701 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4708 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4710 framepos_t const p = _region_view->region()->position ();
4711 double const y = _region_view->midi_view()->y_position ();
4713 x1 = max ((framepos_t) 0, x1 - p);
4714 x2 = max ((framepos_t) 0, x2 - p);
4715 y1 = max (0.0, y1 - y);
4716 y2 = max (0.0, y2 - y);
4718 _region_view->update_drag_selection (
4719 _editor->frame_to_pixel (x1),
4720 _editor->frame_to_pixel (x2),
4723 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4728 MidiRubberbandSelectDrag::deselect_things ()
4733 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4734 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4737 _vertical_only = true;
4741 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4743 double const y = _region_view->midi_view()->y_position ();
4745 y1 = max (0.0, y1 - y);
4746 y2 = max (0.0, y2 - y);
4748 _region_view->update_vertical_drag_selection (
4751 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4756 MidiVerticalSelectDrag::deselect_things ()
4761 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4762 : RubberbandSelectDrag (e, i)
4768 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4770 if (drag_in_progress) {
4771 /* We just want to select things at the end of the drag, not during it */
4775 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4777 _editor->begin_reversible_command (_("rubberband selection"));
4778 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4779 _editor->commit_reversible_command ();
4783 EditorRubberbandSelectDrag::deselect_things ()
4785 if (!getenv("ARDOUR_SAE")) {
4786 _editor->selection->clear_tracks();
4788 _editor->selection->clear_regions();
4789 _editor->selection->clear_points ();
4790 _editor->selection->clear_lines ();
4793 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4801 NoteCreateDrag::~NoteCreateDrag ()
4807 NoteCreateDrag::grid_frames (framepos_t t) const
4810 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4815 return _region_view->region_beats_to_region_frames (grid_beats);
4819 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4821 Drag::start_grab (event, cursor);
4823 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4825 framepos_t pf = _drags->current_pointer_frame ();
4826 framecnt_t const g = grid_frames (pf);
4828 /* Hack so that we always snap to the note that we are over, instead of snapping
4829 to the next one if we're more than halfway through the one we're over.
4831 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4835 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4837 MidiStreamView* sv = _region_view->midi_stream_view ();
4838 double const x = _editor->frame_to_pixel (_note[0]);
4839 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4841 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4842 _drag_rect->set_outline_what (0xff);
4843 _drag_rect->set_outline_color (0xffffff99);
4844 _drag_rect->set_fill_color (0xffffff66);
4848 NoteCreateDrag::motion (GdkEvent* event, bool)
4850 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4851 double const x = _editor->frame_to_pixel (_note[1]);
4852 if (_note[1] > _note[0]) {
4853 _drag_rect->set_x1 (x);
4855 _drag_rect->set_x0 (x);
4860 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4862 if (!had_movement) {
4866 framepos_t const start = min (_note[0], _note[1]);
4867 framecnt_t length = (framecnt_t) fabs (_note[0] - _note[1]);
4869 framecnt_t const g = grid_frames (start);
4870 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4872 if (_editor->snap_mode() == SnapNormal && length < g) {
4873 length = g - one_tick;
4876 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4878 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
4882 NoteCreateDrag::y_to_region (double y) const
4885 _region_view->get_canvas_group()->canvas_to_item (x, y);
4890 NoteCreateDrag::aborted (bool)
4895 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4900 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
4904 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4906 Drag::start_grab (event, cursor);
4910 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4916 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4919 distance = _drags->current_pointer_x() - grab_x();
4920 len = ar->fade_in()->back()->when;
4922 distance = grab_x() - _drags->current_pointer_x();
4923 len = ar->fade_out()->back()->when;
4926 /* how long should it be ? */
4928 new_length = len + _editor->unit_to_frame (distance);
4930 /* now check with the region that this is legal */
4932 new_length = ar->verify_xfade_bounds (new_length, start);
4935 arv->redraw_start_xfade_to (ar, new_length);
4937 arv->redraw_end_xfade_to (ar, new_length);
4942 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
4948 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4951 distance = _drags->current_pointer_x() - grab_x();
4952 len = ar->fade_in()->back()->when;
4954 distance = grab_x() - _drags->current_pointer_x();
4955 len = ar->fade_out()->back()->when;
4958 new_length = ar->verify_xfade_bounds (len + _editor->unit_to_frame (distance), start);
4960 _editor->begin_reversible_command ("xfade trim");
4961 ar->playlist()->clear_owned_changes ();
4964 ar->set_fade_in_length (new_length);
4966 ar->set_fade_out_length (new_length);
4969 /* Adjusting the xfade may affect other regions in the playlist, so we need
4970 to get undo Commands from the whole playlist rather than just the
4974 vector<Command*> cmds;
4975 ar->playlist()->rdiff (cmds);
4976 _editor->session()->add_commands (cmds);
4977 _editor->commit_reversible_command ();
4982 CrossfadeEdgeDrag::aborted (bool)
4985 arv->redraw_start_xfade ();
4987 arv->redraw_end_xfade ();