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/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
49 #include "audio_region_view.h"
50 #include "midi_region_view.h"
51 #include "ardour_ui.h"
52 #include "gui_thread.h"
53 #include "control_point.h"
54 #include "region_gain_line.h"
55 #include "editor_drag.h"
56 #include "audio_time_axis.h"
57 #include "midi_time_axis.h"
58 #include "selection.h"
59 #include "midi_selection.h"
60 #include "automation_time_axis.h"
62 #include "editor_cursors.h"
63 #include "mouse_cursors.h"
64 #include "note_base.h"
65 #include "patch_change.h"
66 #include "verbose_cursor.h"
69 using namespace ARDOUR;
72 using namespace Gtkmm2ext;
73 using namespace Editing;
74 using namespace ArdourCanvas;
76 using Gtkmm2ext::Keyboard;
78 double ControlPointDrag::_zero_gain_fraction = -1.0;
80 DragManager::DragManager (Editor* e)
83 , _current_pointer_frame (0)
87 DragManager::~DragManager ()
92 /** Call abort for each active drag */
98 cerr << "Aborting drag\n";
100 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
105 if (!_drags.empty ()) {
106 _editor->set_follow_playhead (_old_follow_playhead, false);
115 DragManager::add (Drag* d)
117 d->set_manager (this);
118 _drags.push_back (d);
122 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
124 d->set_manager (this);
125 _drags.push_back (d);
130 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
132 /* Prevent follow playhead during the drag to be nice to the user */
133 _old_follow_playhead = _editor->follow_playhead ();
134 _editor->set_follow_playhead (false);
136 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
138 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
139 (*i)->start_grab (e, c);
143 /** Call end_grab for each active drag.
144 * @return true if any drag reported movement having occurred.
147 DragManager::end_grab (GdkEvent* e)
152 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
153 bool const t = (*i)->end_grab (e);
164 _editor->set_follow_playhead (_old_follow_playhead, false);
170 DragManager::mark_double_click ()
172 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
173 (*i)->set_double_click (true);
178 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
182 /* calling this implies that we expect the event to have canvas
185 * Can we guarantee that this is true?
188 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
190 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
191 bool const t = (*i)->motion_handler (e, from_autoscroll);
192 /* run all handlers; return true if at least one of them
193 returns true (indicating that the event has been handled).
205 DragManager::have_item (ArdourCanvas::Item* i) const
207 list<Drag*>::const_iterator j = _drags.begin ();
208 while (j != _drags.end() && (*j)->item () != i) {
212 return j != _drags.end ();
215 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
218 , _pointer_frame_offset (0)
219 , _trackview_only (trackview_only)
220 , _move_threshold_passed (false)
221 , _was_double_click (false)
222 , _raw_grab_frame (0)
224 , _last_pointer_frame (0)
230 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
243 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
245 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
247 if (Keyboard::is_button2_event (&event->button)) {
248 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
249 _y_constrained = true;
250 _x_constrained = false;
252 _y_constrained = false;
253 _x_constrained = true;
256 _x_constrained = false;
257 _y_constrained = false;
260 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
261 setup_pointer_frame_offset ();
262 _grab_frame = adjusted_frame (_raw_grab_frame, event);
263 _last_pointer_frame = _grab_frame;
264 _last_pointer_x = _grab_x;
266 if (_trackview_only) {
267 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
270 _last_pointer_y = _grab_y;
275 /* CAIROCANVAS need a variant here that passes *cursor */
277 _cursor_ctx = CursorContext::create(*_editor, cursor);
280 if (_editor->session() && _editor->session()->transport_rolling()) {
283 _was_rolling = false;
286 switch (_editor->snap_type()) {
287 case SnapToRegionStart:
288 case SnapToRegionEnd:
289 case SnapToRegionSync:
290 case SnapToRegionBoundary:
291 _editor->build_region_boundary_cache ();
298 /** Call to end a drag `successfully'. Ungrabs item and calls
299 * subclass' finished() method.
301 * @param event GDK event, or 0.
302 * @return true if some movement occurred, otherwise false.
305 Drag::end_grab (GdkEvent* event)
307 _editor->stop_canvas_autoscroll ();
311 finished (event, _move_threshold_passed);
313 _editor->verbose_cursor()->hide ();
316 return _move_threshold_passed;
320 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
324 if (f > _pointer_frame_offset) {
325 pos = f - _pointer_frame_offset;
329 _editor->snap_to_with_modifier (pos, event);
336 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
338 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
342 Drag::current_pointer_x() const
344 return _drags->current_pointer_x ();
348 Drag::current_pointer_y () const
350 if (!_trackview_only) {
351 return _drags->current_pointer_y ();
354 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
358 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
360 /* check to see if we have moved in any way that matters since the last motion event */
361 if (_move_threshold_passed &&
362 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
363 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
367 pair<framecnt_t, int> const threshold = move_threshold ();
369 bool const old_move_threshold_passed = _move_threshold_passed;
371 if (!from_autoscroll && !_move_threshold_passed) {
373 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
374 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
376 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
379 if (active (_editor->mouse_mode) && _move_threshold_passed) {
381 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
382 if (!from_autoscroll) {
383 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
386 if (!_editor->autoscroll_active() || from_autoscroll) {
387 motion (event, _move_threshold_passed != old_move_threshold_passed);
389 _last_pointer_x = _drags->current_pointer_x ();
390 _last_pointer_y = current_pointer_y ();
391 _last_pointer_frame = adjusted_current_frame (event);
401 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
409 aborted (_move_threshold_passed);
411 _editor->stop_canvas_autoscroll ();
412 _editor->verbose_cursor()->hide ();
416 Drag::show_verbose_cursor_time (framepos_t frame)
418 _editor->verbose_cursor()->set_time (frame);
419 _editor->verbose_cursor()->show ();
423 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
425 _editor->verbose_cursor()->set_duration (start, end);
426 _editor->verbose_cursor()->show ();
430 Drag::show_verbose_cursor_text (string const & text)
432 _editor->verbose_cursor()->set (text);
433 _editor->verbose_cursor()->show ();
436 boost::shared_ptr<Region>
437 Drag::add_midi_region (MidiTimeAxisView* view)
439 if (_editor->session()) {
440 const TempoMap& map (_editor->session()->tempo_map());
441 framecnt_t pos = grab_frame();
442 const Meter& m = map.meter_at (pos);
443 /* not that the frame rate used here can be affected by pull up/down which
446 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
447 return view->add_region (grab_frame(), len, true);
450 return boost::shared_ptr<Region>();
453 struct EditorOrderTimeAxisViewSorter {
454 bool operator() (TimeAxisView* a, TimeAxisView* b) {
455 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
456 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
458 return ra->route()->order_key () < rb->route()->order_key ();
462 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
466 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
468 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
469 as some of the regions we are dragging may be on such tracks.
472 TrackViewList track_views = _editor->track_views;
473 track_views.sort (EditorOrderTimeAxisViewSorter ());
475 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
476 _time_axis_views.push_back (*i);
478 TimeAxisView::Children children_list = (*i)->get_child_list ();
479 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
480 _time_axis_views.push_back (j->get());
484 /* the list of views can be empty at this point if this is a region list-insert drag
487 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
488 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
491 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
495 RegionDrag::region_going_away (RegionView* v)
497 list<DraggingView>::iterator i = _views.begin ();
498 while (i != _views.end() && i->view != v) {
502 if (i != _views.end()) {
507 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
508 * or -1 if it is not found.
511 RegionDrag::find_time_axis_view (TimeAxisView* t) const
514 int const N = _time_axis_views.size ();
515 while (i < N && _time_axis_views[i] != t) {
526 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
527 : RegionDrag (e, i, p, v)
530 , _last_pointer_time_axis_view (0)
531 , _last_pointer_layer (0)
533 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
537 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
539 Drag::start_grab (event, cursor);
541 show_verbose_cursor_time (_last_frame_position);
543 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
545 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
546 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
551 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
553 /* compute the amount of pointer motion in frames, and where
554 the region would be if we moved it by that much.
556 *pending_region_position = adjusted_current_frame (event);
558 framepos_t sync_frame;
559 framecnt_t sync_offset;
562 sync_offset = _primary->region()->sync_offset (sync_dir);
564 /* we don't handle a sync point that lies before zero.
566 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
568 sync_frame = *pending_region_position + (sync_dir*sync_offset);
570 _editor->snap_to_with_modifier (sync_frame, event);
572 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
575 *pending_region_position = _last_frame_position;
578 if (*pending_region_position > max_framepos - _primary->region()->length()) {
579 *pending_region_position = _last_frame_position;
584 /* in locked edit mode, reverse the usual meaning of _x_constrained */
585 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
587 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
589 /* x movement since last time (in pixels) */
590 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
592 /* total x movement */
593 framecnt_t total_dx = *pending_region_position;
594 if (regions_came_from_canvas()) {
595 total_dx = total_dx - grab_frame ();
598 /* check that no regions have gone off the start of the session */
599 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
600 if ((i->view->region()->position() + total_dx) < 0) {
602 *pending_region_position = _last_frame_position;
613 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
615 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
616 int const n = i->time_axis_view + delta_track;
617 if (n < 0 || n >= int (_time_axis_views.size())) {
618 /* off the top or bottom track */
622 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
623 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
624 /* not a track, or the wrong type */
628 double const l = i->layer + delta_layer;
630 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
631 mode to allow the user to place a region below another on layer 0.
633 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
634 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
635 If it has, the layers will be munged later anyway, so it's ok.
641 /* all regions being dragged are ok with this change */
646 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
648 double delta_layer = 0;
649 int delta_time_axis_view = 0;
651 assert (!_views.empty ());
653 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
655 /* Find the TimeAxisView that the pointer is now over */
656 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
657 TimeAxisView* tv = r.first;
659 if (tv && tv->view()) {
660 double layer = r.second;
662 if (first_move && tv->view()->layer_display() == Stacked) {
663 tv->view()->set_layer_display (Expanded);
666 /* Here's the current pointer position in terms of time axis view and layer */
667 int const current_pointer_time_axis_view = find_time_axis_view (tv);
668 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
670 /* Work out the change in y */
672 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
673 delta_layer = current_pointer_layer - _last_pointer_layer;
676 /* Work out the change in x */
677 framepos_t pending_region_position;
678 double const x_delta = compute_x_delta (event, &pending_region_position);
679 _last_frame_position = pending_region_position;
681 /* Verify change in y */
682 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
683 /* this y movement is not allowed, so do no y movement this time */
684 delta_time_axis_view = 0;
688 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
689 /* haven't reached next snap point, and we're not switching
690 trackviews nor layers. nothing to do.
695 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
697 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
699 RegionView* rv = i->view;
701 if (rv->region()->locked() || rv->region()->video_locked()) {
708 /* reparent the regionview into a group above all
712 ArdourCanvas::Item* rvg = rv->get_canvas_group();
713 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
714 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
715 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
716 /* move the item so that it continues to appear at the
717 same location now that its parent has changed.
719 rvg->move (rv_canvas_offset - dmg_canvas_offset);
722 /* If we have moved tracks, we'll fudge the layer delta so that the
723 region gets moved back onto layer 0 on its new track; this avoids
724 confusion when dragging regions from non-zero layers onto different
727 double this_delta_layer = delta_layer;
728 if (delta_time_axis_view != 0) {
729 this_delta_layer = - i->layer;
736 if (i->time_axis_view >= 0) {
737 track_index = i->time_axis_view + delta_time_axis_view;
739 track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
742 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
746 /* The TimeAxisView that this region is now over */
747 TimeAxisView* current_tv = _time_axis_views[track_index];
749 /* Ensure it is moved from stacked -> expanded if appropriate */
750 if (current_tv->view()->layer_display() == Stacked) {
751 current_tv->view()->set_layer_display (Expanded);
754 /* We're only allowed to go -ve in layer on Expanded views */
755 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
756 this_delta_layer = - i->layer;
760 rv->set_height (current_tv->view()->child_height ());
762 /* Update show/hidden status as the region view may have come from a hidden track,
763 or have moved to one.
765 if (current_tv->hidden ()) {
766 rv->get_canvas_group()->hide ();
768 rv->get_canvas_group()->show ();
771 /* Update the DraggingView */
772 i->time_axis_view = track_index;
773 i->layer += this_delta_layer;
776 _editor->mouse_brush_insert_region (rv, pending_region_position);
780 /* Get the y coordinate of the top of the track that this region is now over */
781 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
783 /* And adjust for the layer that it should be on */
784 StreamView* cv = current_tv->view ();
785 switch (cv->layer_display ()) {
789 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
792 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
796 /* need to get the parent of the regionview
797 * canvas group and get its position in
798 * equivalent coordinate space as the trackview
799 * we are now dragging over.
802 /* Now move the region view */
803 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->canvas_origin().y);
807 /* Only move the region into the empty dropzone at the bottom if the pointer
811 if (current_pointer_y() >= 0) {
813 Coord last_track_bottom_edge;
814 if (!_time_axis_views.empty()) {
815 TimeAxisView* last = _time_axis_views.back();
816 last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
818 last_track_bottom_edge = 0;
821 rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y);
822 i->time_axis_view = -1;
826 } /* foreach region */
828 _total_x_delta += x_delta;
830 if (x_delta != 0 && !_brushing) {
831 show_verbose_cursor_time (_last_frame_position);
834 _last_pointer_time_axis_view += delta_time_axis_view;
835 _last_pointer_layer += delta_layer;
839 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
841 if (_copy && first_move) {
843 if (_x_constrained) {
844 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
846 _editor->begin_reversible_command (Operations::region_copy);
849 /* duplicate the regionview(s) and region(s) */
851 list<DraggingView> new_regionviews;
853 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
855 RegionView* rv = i->view;
856 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
857 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
859 const boost::shared_ptr<const Region> original = rv->region();
860 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
861 region_copy->set_position (original->position());
865 boost::shared_ptr<AudioRegion> audioregion_copy
866 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
868 nrv = new AudioRegionView (*arv, audioregion_copy);
870 boost::shared_ptr<MidiRegion> midiregion_copy
871 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
872 nrv = new MidiRegionView (*mrv, midiregion_copy);
877 nrv->get_canvas_group()->show ();
878 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
880 /* swap _primary to the copy */
882 if (rv == _primary) {
886 /* ..and deselect the one we copied */
888 rv->set_selected (false);
891 if (!new_regionviews.empty()) {
893 /* reflect the fact that we are dragging the copies */
895 _views = new_regionviews;
897 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
900 } else if (!_copy && first_move) {
902 if (_x_constrained) {
903 _editor->begin_reversible_command (_("fixed time region drag"));
905 _editor->begin_reversible_command (Operations::region_drag);
909 RegionMotionDrag::motion (event, first_move);
913 RegionMotionDrag::finished (GdkEvent *, bool)
915 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
920 if ((*i)->view()->layer_display() == Expanded) {
921 (*i)->view()->set_layer_display (Stacked);
927 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
929 RegionMotionDrag::finished (ev, movement_occurred);
931 if (!movement_occurred) {
935 if (was_double_click() && !_views.empty()) {
936 DraggingView dv = _views.front();
937 dv.view->show_region_editor ();
944 /* reverse this here so that we have the correct logic to finalize
948 if (Config->get_edit_mode() == Lock) {
949 _x_constrained = !_x_constrained;
952 assert (!_views.empty ());
954 /* We might have hidden region views so that they weren't visible during the drag
955 (when they have been reparented). Now everything can be shown again, as region
956 views are back in their track parent groups.
958 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
959 i->view->get_canvas_group()->show ();
962 bool const changed_position = (_last_frame_position != _primary->region()->position());
963 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
964 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
984 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
988 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
990 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
995 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
996 list<boost::shared_ptr<AudioTrack> > audio_tracks;
997 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
998 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1000 rtav->set_height (original->current_height());
1004 ChanCount one_midi_port (DataType::MIDI, 1);
1005 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1006 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1007 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1009 rtav->set_height (original->current_height());
1014 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1020 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1022 RegionSelection new_views;
1023 PlaylistSet modified_playlists;
1024 RouteTimeAxisView* new_time_axis_view = 0;
1027 /* all changes were made during motion event handlers */
1029 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1033 _editor->commit_reversible_command ();
1037 /* insert the regions into their new playlists */
1038 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1040 RouteTimeAxisView* dest_rtv = 0;
1042 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1048 if (changed_position && !_x_constrained) {
1049 where = i->view->region()->position() - drag_delta;
1051 where = i->view->region()->position();
1054 if (i->time_axis_view < 0) {
1055 if (!new_time_axis_view) {
1056 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1058 dest_rtv = new_time_axis_view;
1060 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1063 if (dest_rtv != 0) {
1064 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1065 if (new_view != 0) {
1066 new_views.push_back (new_view);
1070 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1071 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1074 list<DraggingView>::const_iterator next = i;
1080 /* If we've created new regions either by copying or moving
1081 to a new track, we want to replace the old selection with the new ones
1084 if (new_views.size() > 0) {
1085 _editor->selection->set (new_views);
1088 /* write commands for the accumulated diffs for all our modified playlists */
1089 add_stateful_diff_commands_for_playlists (modified_playlists);
1091 _editor->commit_reversible_command ();
1095 RegionMoveDrag::finished_no_copy (
1096 bool const changed_position,
1097 bool const changed_tracks,
1098 framecnt_t const drag_delta
1101 RegionSelection new_views;
1102 PlaylistSet modified_playlists;
1103 PlaylistSet frozen_playlists;
1104 set<RouteTimeAxisView*> views_to_update;
1105 RouteTimeAxisView* new_time_axis_view = 0;
1108 /* all changes were made during motion event handlers */
1109 _editor->commit_reversible_command ();
1113 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1115 RegionView* rv = i->view;
1116 RouteTimeAxisView* dest_rtv = 0;
1118 if (rv->region()->locked() || rv->region()->video_locked()) {
1123 if (i->time_axis_view < 0) {
1124 if (!new_time_axis_view) {
1125 new_time_axis_view = create_destination_time_axis (rv->region(), i->initial_time_axis_view);
1127 dest_rtv = new_time_axis_view;
1129 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1134 double const dest_layer = i->layer;
1136 views_to_update.insert (dest_rtv);
1140 if (changed_position && !_x_constrained) {
1141 where = rv->region()->position() - drag_delta;
1143 where = rv->region()->position();
1146 if (changed_tracks) {
1148 /* insert into new playlist */
1150 RegionView* new_view = insert_region_into_playlist (
1151 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1154 if (new_view == 0) {
1159 new_views.push_back (new_view);
1161 /* remove from old playlist */
1163 /* the region that used to be in the old playlist is not
1164 moved to the new one - we use a copy of it. as a result,
1165 any existing editor for the region should no longer be
1168 rv->hide_region_editor();
1171 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1175 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1177 /* this movement may result in a crossfade being modified, or a layering change,
1178 so we need to get undo data from the playlist as well as the region.
1181 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1183 playlist->clear_changes ();
1186 rv->region()->clear_changes ();
1189 motion on the same track. plonk the previously reparented region
1190 back to its original canvas group (its streamview).
1191 No need to do anything for copies as they are fake regions which will be deleted.
1194 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1195 rv->get_canvas_group()->set_y_position (i->initial_y);
1198 /* just change the model */
1199 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1200 playlist->set_layer (rv->region(), dest_layer);
1203 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1205 r = frozen_playlists.insert (playlist);
1208 playlist->freeze ();
1211 rv->region()->set_position (where);
1213 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1216 if (changed_tracks) {
1218 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1219 was selected in all of them, then removing it from a playlist will have removed all
1220 trace of it from _views (i.e. there were N regions selected, we removed 1,
1221 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1222 corresponding regionview, and _views is now empty).
1224 This could have invalidated any and all iterators into _views.
1226 The heuristic we use here is: if the region selection is empty, break out of the loop
1227 here. if the region selection is not empty, then restart the loop because we know that
1228 we must have removed at least the region(view) we've just been working on as well as any
1229 that we processed on previous iterations.
1231 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1232 we can just iterate.
1236 if (_views.empty()) {
1247 /* If we've created new regions either by copying or moving
1248 to a new track, we want to replace the old selection with the new ones
1251 if (new_views.size() > 0) {
1252 _editor->selection->set (new_views);
1255 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1259 /* write commands for the accumulated diffs for all our modified playlists */
1260 add_stateful_diff_commands_for_playlists (modified_playlists);
1262 _editor->commit_reversible_command ();
1264 /* We have futzed with the layering of canvas items on our streamviews.
1265 If any region changed layer, this will have resulted in the stream
1266 views being asked to set up their region views, and all will be well.
1267 If not, we might now have badly-ordered region views. Ask the StreamViews
1268 involved to sort themselves out, just in case.
1271 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1272 (*i)->view()->playlist_layered ((*i)->track ());
1276 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1277 * @param region Region to remove.
1278 * @param playlist playlist To remove from.
1279 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1280 * that clear_changes () is only called once per playlist.
1283 RegionMoveDrag::remove_region_from_playlist (
1284 boost::shared_ptr<Region> region,
1285 boost::shared_ptr<Playlist> playlist,
1286 PlaylistSet& modified_playlists
1289 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1292 playlist->clear_changes ();
1295 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1299 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1300 * clearing the playlist's diff history first if necessary.
1301 * @param region Region to insert.
1302 * @param dest_rtv Destination RouteTimeAxisView.
1303 * @param dest_layer Destination layer.
1304 * @param where Destination position.
1305 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1306 * that clear_changes () is only called once per playlist.
1307 * @return New RegionView, or 0 if no insert was performed.
1310 RegionMoveDrag::insert_region_into_playlist (
1311 boost::shared_ptr<Region> region,
1312 RouteTimeAxisView* dest_rtv,
1315 PlaylistSet& modified_playlists
1318 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1319 if (!dest_playlist) {
1323 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1324 _new_region_view = 0;
1325 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1327 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1328 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1330 dest_playlist->clear_changes ();
1333 dest_playlist->add_region (region, where);
1335 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1336 dest_playlist->set_layer (region, dest_layer);
1341 assert (_new_region_view);
1343 return _new_region_view;
1347 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1349 _new_region_view = rv;
1353 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1355 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1356 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1358 _editor->session()->add_command (c);
1367 RegionMoveDrag::aborted (bool movement_occurred)
1371 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1378 RegionMotionDrag::aborted (movement_occurred);
1383 RegionMotionDrag::aborted (bool)
1385 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1387 StreamView* sview = (*i)->view();
1390 if (sview->layer_display() == Expanded) {
1391 sview->set_layer_display (Stacked);
1396 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1397 RegionView* rv = i->view;
1398 TimeAxisView* tv = &(rv->get_time_axis_view ());
1399 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1401 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1402 rv->get_canvas_group()->set_y_position (0);
1404 rv->move (-_total_x_delta, 0);
1405 rv->set_height (rtv->view()->child_height ());
1409 /** @param b true to brush, otherwise false.
1410 * @param c true to make copies of the regions being moved, otherwise false.
1412 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1413 : RegionMotionDrag (e, i, p, v, b),
1416 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1419 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1420 if (rtv && rtv->is_track()) {
1421 speed = rtv->track()->speed ();
1424 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1428 RegionMoveDrag::setup_pointer_frame_offset ()
1430 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1433 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1434 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1436 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1438 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1439 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1441 _primary = v->view()->create_region_view (r, false, false);
1443 _primary->get_canvas_group()->show ();
1444 _primary->set_position (pos, 0);
1445 _views.push_back (DraggingView (_primary, this, v));
1447 _last_frame_position = pos;
1449 _item = _primary->get_canvas_group ();
1453 RegionInsertDrag::finished (GdkEvent *, bool)
1455 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1457 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1458 _primary->get_canvas_group()->set_y_position (0);
1460 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1462 _editor->begin_reversible_command (Operations::insert_region);
1463 playlist->clear_changes ();
1464 playlist->add_region (_primary->region (), _last_frame_position);
1466 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1467 if (Config->get_edit_mode() == Ripple) {
1468 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1471 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1472 _editor->commit_reversible_command ();
1480 RegionInsertDrag::aborted (bool)
1487 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1488 : RegionMoveDrag (e, i, p, v, false, false)
1490 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1493 struct RegionSelectionByPosition {
1494 bool operator() (RegionView*a, RegionView* b) {
1495 return a->region()->position () < b->region()->position();
1500 RegionSpliceDrag::motion (GdkEvent* event, bool)
1502 /* Which trackview is this ? */
1504 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1505 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1507 /* The region motion is only processed if the pointer is over
1511 if (!tv || !tv->is_track()) {
1512 /* To make sure we hide the verbose canvas cursor when the mouse is
1513 not held over an audio track.
1515 _editor->verbose_cursor()->hide ();
1518 _editor->verbose_cursor()->show ();
1523 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1529 RegionSelection copy;
1530 _editor->selection->regions.by_position(copy);
1532 framepos_t const pf = adjusted_current_frame (event);
1534 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1536 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1542 boost::shared_ptr<Playlist> playlist;
1544 if ((playlist = atv->playlist()) == 0) {
1548 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1553 if (pf < (*i)->region()->last_frame() + 1) {
1557 if (pf > (*i)->region()->first_frame()) {
1563 playlist->shuffle ((*i)->region(), dir);
1568 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1570 RegionMoveDrag::finished (event, movement_occurred);
1574 RegionSpliceDrag::aborted (bool)
1584 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1587 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1589 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1590 RegionSelection to_ripple;
1591 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1592 if ((*i)->position() >= where) {
1593 to_ripple.push_back (rtv->view()->find_view(*i));
1597 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1598 if (!exclude.contains (*i)) {
1599 // the selection has already been added to _views
1601 if (drag_in_progress) {
1602 // do the same things that RegionMotionDrag::motion does when
1603 // first_move is true, for the region views that we're adding
1604 // to _views this time
1607 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1608 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1609 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1610 rvg->reparent (_editor->_drag_motion_group);
1612 // we only need to move in the y direction
1613 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1618 _views.push_back (DraggingView (*i, this, tav));
1624 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1627 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1628 // we added all the regions after the selection
1630 std::list<DraggingView>::iterator to_erase = i++;
1631 if (!_editor->selection->regions.contains (to_erase->view)) {
1632 // restore the non-selected regions to their original playlist & positions,
1633 // and then ripple them back by the length of the regions that were dragged away
1634 // do the same things as RegionMotionDrag::aborted
1636 RegionView *rv = to_erase->view;
1637 TimeAxisView* tv = &(rv->get_time_axis_view ());
1638 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1641 // plonk them back onto their own track
1642 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1643 rv->get_canvas_group()->set_y_position (0);
1647 // move the underlying region to match the view
1648 rv->region()->set_position (rv->region()->position() + amount);
1650 // restore the view to match the underlying region's original position
1651 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
1654 rv->set_height (rtv->view()->child_height ());
1655 _views.erase (to_erase);
1661 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const
1663 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) {
1665 return allow_moves_across_tracks;
1673 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1674 : RegionMoveDrag (e, i, p, v, false, false)
1676 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
1677 // compute length of selection
1678 RegionSelection selected_regions = _editor->selection->regions;
1679 selection_length = selected_regions.end_frame() - selected_regions.start();
1681 // we'll only allow dragging to another track in ripple mode if all the regions
1682 // being dragged start off on the same track
1683 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
1686 exclude = new RegionList;
1687 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
1688 exclude->push_back((*i)->region());
1691 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
1692 RegionSelection copy;
1693 selected_regions.by_position(copy); // get selected regions sorted by position into copy
1695 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
1696 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1698 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1699 // find ripple start point on each applicable playlist
1700 RegionView *first_selected_on_this_track = NULL;
1701 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1702 if ((*i)->region()->playlist() == (*pi)) {
1703 // region is on this playlist - it's the first, because they're sorted
1704 first_selected_on_this_track = *i;
1708 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
1709 add_all_after_to_views (
1710 &first_selected_on_this_track->get_time_axis_view(),
1711 first_selected_on_this_track->region()->position(),
1712 selected_regions, false);
1715 if (allow_moves_across_tracks) {
1716 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
1724 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
1726 /* Which trackview is this ? */
1728 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1729 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1731 /* The region motion is only processed if the pointer is over
1735 if (!tv || !tv->is_track()) {
1736 /* To make sure we hide the verbose canvas cursor when the mouse is
1737 not held over an audiotrack.
1739 _editor->verbose_cursor()->hide ();
1743 framepos_t where = adjusted_current_frame (event);
1744 assert (where >= 0);
1746 double delta = compute_x_delta (event, &after);
1748 framecnt_t amount = _editor->pixel_to_sample (delta);
1750 if (allow_moves_across_tracks) {
1751 // all the originally selected regions were on the same track
1753 framecnt_t adjust = 0;
1754 if (prev_tav && tv != prev_tav) {
1755 // dragged onto a different track
1756 // remove the unselected regions from _views, restore them to their original positions
1757 // and add the regions after the drop point on the new playlist to _views instead.
1758 // undo the effect of rippling the previous playlist, and include the effect of removing
1759 // the dragged region(s) from this track
1761 remove_unselected_from_views (prev_amount, false);
1762 // ripple previous playlist according to the regions that have been removed onto the new playlist
1763 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
1766 // move just the selected regions
1767 RegionMoveDrag::motion(event, first_move);
1769 // ensure that the ripple operation on the new playlist inserts selection_length time
1770 adjust = selection_length;
1771 // ripple the new current playlist
1772 tv->playlist()->ripple (where, amount+adjust, exclude);
1774 // add regions after point where drag entered this track to subsequent ripples
1775 add_all_after_to_views (tv, where, _editor->selection->regions, true);
1778 // motion on same track
1779 RegionMoveDrag::motion(event, first_move);
1783 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
1784 prev_position = where;
1786 // selection encompasses multiple tracks - just drag
1787 // cross-track drags are forbidden
1788 RegionMoveDrag::motion(event, first_move);
1791 if (!_x_constrained) {
1792 prev_amount += amount;
1795 _last_frame_position = after;
1799 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
1801 if (!movement_occurred) {
1805 if (was_double_click() && !_views.empty()) {
1806 DraggingView dv = _views.front();
1807 dv.view->show_region_editor ();
1814 _editor->begin_reversible_command(_("Ripple drag"));
1816 // remove the regions being rippled from the dragging view, updating them to
1817 // their new positions
1818 remove_unselected_from_views (prev_amount, true);
1820 if (allow_moves_across_tracks) {
1822 // if regions were dragged across tracks, we've rippled any later
1823 // regions on the track the regions were dragged off, so we need
1824 // to add the original track to the undo record
1825 orig_tav->playlist()->clear_changes();
1826 vector<Command*> cmds;
1827 orig_tav->playlist()->rdiff (cmds);
1828 _editor->session()->add_commands (cmds);
1830 if (prev_tav && prev_tav != orig_tav) {
1831 prev_tav->playlist()->clear_changes();
1832 vector<Command*> cmds;
1833 prev_tav->playlist()->rdiff (cmds);
1834 _editor->session()->add_commands (cmds);
1837 // selection spanned multiple tracks - all will need adding to undo record
1839 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
1840 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1842 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1843 (*pi)->clear_changes();
1844 vector<Command*> cmds;
1845 (*pi)->rdiff (cmds);
1846 _editor->session()->add_commands (cmds);
1850 // other modified playlists are added to undo by RegionMoveDrag::finished()
1851 RegionMoveDrag::finished (event, movement_occurred);
1852 _editor->commit_reversible_command();
1856 RegionRippleDrag::aborted (bool movement_occurred)
1858 RegionMoveDrag::aborted (movement_occurred);
1863 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1865 _view (dynamic_cast<MidiTimeAxisView*> (v))
1867 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1873 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1876 _region = add_midi_region (_view);
1877 _view->playlist()->freeze ();
1880 framepos_t const f = adjusted_current_frame (event);
1881 if (f < grab_frame()) {
1882 _region->set_position (f);
1885 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1886 so that if this region is duplicated, its duplicate starts on
1887 a snap point rather than 1 frame after a snap point. Otherwise things get
1888 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1889 place snapped notes at the start of the region.
1892 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1893 _region->set_length (len < 1 ? 1 : len);
1899 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1901 if (!movement_occurred) {
1902 add_midi_region (_view);
1904 _view->playlist()->thaw ();
1909 RegionCreateDrag::aborted (bool)
1912 _view->playlist()->thaw ();
1918 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1922 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1926 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1928 Gdk::Cursor* cursor;
1929 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1931 float x_fraction = cnote->mouse_x_fraction ();
1933 if (x_fraction > 0.0 && x_fraction < 0.25) {
1934 cursor = _editor->cursors()->left_side_trim;
1937 cursor = _editor->cursors()->right_side_trim;
1941 Drag::start_grab (event, cursor);
1943 region = &cnote->region_view();
1947 if (event->motion.state & Keyboard::PrimaryModifier) {
1953 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1955 if (ms.size() > 1) {
1956 /* has to be relative, may make no sense otherwise */
1960 /* select this note; if it is already selected, preserve the existing selection,
1961 otherwise make this note the only one selected.
1963 region->note_selected (cnote, cnote->selected ());
1965 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1966 MidiRegionSelection::iterator next;
1969 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
1971 mrv->begin_resizing (at_front);
1978 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1980 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1981 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1982 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1984 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
1986 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1992 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1994 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1995 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1996 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1998 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2000 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2006 NoteResizeDrag::aborted (bool)
2008 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2009 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2010 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2012 mrv->abort_resizing ();
2017 AVDraggingView::AVDraggingView (RegionView* v)
2020 initial_position = v->region()->position ();
2023 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2026 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2029 TrackViewList empty;
2031 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2032 std::list<RegionView*> views = rs.by_layer();
2034 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2035 RegionView* rv = (*i);
2036 if (!rv->region()->video_locked()) {
2039 _views.push_back (AVDraggingView (rv));
2044 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2046 Drag::start_grab (event);
2047 if (_editor->session() == 0) {
2051 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2052 _max_backwards_drag = (
2053 ARDOUR_UI::instance()->video_timeline->get_duration()
2054 + ARDOUR_UI::instance()->video_timeline->get_offset()
2055 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2058 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2059 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2060 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2063 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2066 Timecode::Time timecode;
2067 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2068 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);
2069 show_verbose_cursor_text (buf);
2073 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2075 if (_editor->session() == 0) {
2078 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2082 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2083 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2085 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2086 dt = - _max_backwards_drag;
2089 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2090 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2092 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2093 RegionView* rv = i->view;
2094 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2097 rv->region()->clear_changes ();
2098 rv->region()->suspend_property_changes();
2100 rv->region()->set_position(i->initial_position + dt);
2101 rv->region_changed(ARDOUR::Properties::position);
2104 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2105 Timecode::Time timecode;
2106 Timecode::Time timediff;
2108 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2109 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2110 snprintf (buf, sizeof (buf),
2111 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2112 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2113 , _("Video Start:"),
2114 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2116 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2118 show_verbose_cursor_text (buf);
2122 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2124 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2128 if (!movement_occurred || ! _editor->session()) {
2132 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2134 _editor->begin_reversible_command (_("Move Video"));
2136 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2137 ARDOUR_UI::instance()->video_timeline->save_undo();
2138 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2139 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2141 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2142 i->view->drag_end();
2143 i->view->region()->resume_property_changes ();
2145 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2148 _editor->session()->maybe_update_session_range(
2149 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2150 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2154 _editor->commit_reversible_command ();
2158 VideoTimeLineDrag::aborted (bool)
2160 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2163 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2164 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2166 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2167 i->view->region()->resume_property_changes ();
2168 i->view->region()->set_position(i->initial_position);
2172 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2173 : RegionDrag (e, i, p, v)
2174 , _preserve_fade_anchor (preserve_fade_anchor)
2175 , _jump_position_when_done (false)
2177 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2181 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2184 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2185 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2187 if (tv && tv->is_track()) {
2188 speed = tv->track()->speed();
2191 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2192 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2193 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2195 framepos_t const pf = adjusted_current_frame (event);
2197 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2198 /* Move the contents of the region around without changing the region bounds */
2199 _operation = ContentsTrim;
2200 Drag::start_grab (event, _editor->cursors()->trimmer);
2202 /* These will get overridden for a point trim.*/
2203 if (pf < (region_start + region_length/2)) {
2204 /* closer to front */
2205 _operation = StartTrim;
2207 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2208 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2210 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2214 _operation = EndTrim;
2215 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2216 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2218 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2223 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2224 _jump_position_when_done = true;
2227 switch (_operation) {
2229 show_verbose_cursor_time (region_start);
2230 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2231 i->view->trim_front_starting ();
2235 show_verbose_cursor_time (region_end);
2238 show_verbose_cursor_time (pf);
2242 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2243 i->view->region()->suspend_property_changes ();
2248 TrimDrag::motion (GdkEvent* event, bool first_move)
2250 RegionView* rv = _primary;
2253 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2254 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2255 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2256 frameoffset_t frame_delta = 0;
2258 if (tv && tv->is_track()) {
2259 speed = tv->track()->speed();
2262 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2268 switch (_operation) {
2270 trim_type = "Region start trim";
2273 trim_type = "Region end trim";
2276 trim_type = "Region content trim";
2283 _editor->begin_reversible_command (trim_type);
2285 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2286 RegionView* rv = i->view;
2287 rv->enable_display (false);
2288 rv->region()->playlist()->clear_owned_changes ();
2290 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2293 arv->temporarily_hide_envelope ();
2297 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2298 insert_result = _editor->motion_frozen_playlists.insert (pl);
2300 if (insert_result.second) {
2306 bool non_overlap_trim = false;
2308 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2309 non_overlap_trim = true;
2312 /* contstrain trim to fade length */
2313 if (_preserve_fade_anchor) {
2314 switch (_operation) {
2316 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2317 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2319 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2320 if (ar->locked()) continue;
2321 framecnt_t len = ar->fade_in()->back()->when;
2322 if (len < dt) dt = min(dt, len);
2326 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2327 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2329 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2330 if (ar->locked()) continue;
2331 framecnt_t len = ar->fade_out()->back()->when;
2332 if (len < -dt) dt = max(dt, -len);
2341 switch (_operation) {
2343 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2344 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2345 if (changed && _preserve_fade_anchor) {
2346 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2348 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2349 framecnt_t len = ar->fade_in()->back()->when;
2350 framecnt_t diff = ar->first_frame() - i->initial_position;
2351 framepos_t new_length = len - diff;
2352 i->anchored_fade_length = min (ar->length(), new_length);
2353 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2354 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2361 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2362 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2363 if (changed && _preserve_fade_anchor) {
2364 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2366 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2367 framecnt_t len = ar->fade_out()->back()->when;
2368 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2369 framepos_t new_length = len + diff;
2370 i->anchored_fade_length = min (ar->length(), new_length);
2371 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2372 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2380 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2382 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2383 i->view->move_contents (frame_delta);
2389 switch (_operation) {
2391 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2394 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2397 // show_verbose_cursor_time (frame_delta);
2404 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2406 if (movement_occurred) {
2407 motion (event, false);
2409 if (_operation == StartTrim) {
2410 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2412 /* This must happen before the region's StatefulDiffCommand is created, as it may
2413 `correct' (ahem) the region's _start from being negative to being zero. It
2414 needs to be zero in the undo record.
2416 i->view->trim_front_ending ();
2418 if (_preserve_fade_anchor) {
2419 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2421 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2422 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2423 ar->set_fade_in_length(i->anchored_fade_length);
2424 ar->set_fade_in_active(true);
2427 if (_jump_position_when_done) {
2428 i->view->region()->set_position (i->initial_position);
2431 } else if (_operation == EndTrim) {
2432 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2433 if (_preserve_fade_anchor) {
2434 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2436 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2437 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2438 ar->set_fade_out_length(i->anchored_fade_length);
2439 ar->set_fade_out_active(true);
2442 if (_jump_position_when_done) {
2443 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2448 if (!_views.empty()) {
2449 if (_operation == StartTrim) {
2450 _editor->maybe_locate_with_edit_preroll(
2451 _views.begin()->view->region()->position());
2453 if (_operation == EndTrim) {
2454 _editor->maybe_locate_with_edit_preroll(
2455 _views.begin()->view->region()->position() +
2456 _views.begin()->view->region()->length());
2460 if (!_editor->selection->selected (_primary)) {
2461 _primary->thaw_after_trim ();
2464 set<boost::shared_ptr<Playlist> > diffed_playlists;
2466 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2467 i->view->thaw_after_trim ();
2468 i->view->enable_display (true);
2470 /* Trimming one region may affect others on the playlist, so we need
2471 to get undo Commands from the whole playlist rather than just the
2472 region. Use diffed_playlists to make sure we don't diff a given
2473 playlist more than once.
2475 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2476 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2477 vector<Command*> cmds;
2479 _editor->session()->add_commands (cmds);
2480 diffed_playlists.insert (p);
2485 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2489 _editor->motion_frozen_playlists.clear ();
2490 _editor->commit_reversible_command();
2493 /* no mouse movement */
2494 _editor->point_trim (event, adjusted_current_frame (event));
2497 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2498 if (_operation == StartTrim) {
2499 i->view->trim_front_ending ();
2502 i->view->region()->resume_property_changes ();
2507 TrimDrag::aborted (bool movement_occurred)
2509 /* Our motion method is changing model state, so use the Undo system
2510 to cancel. Perhaps not ideal, as this will leave an Undo point
2511 behind which may be slightly odd from the user's point of view.
2516 if (movement_occurred) {
2520 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2521 i->view->region()->resume_property_changes ();
2526 TrimDrag::setup_pointer_frame_offset ()
2528 list<DraggingView>::iterator i = _views.begin ();
2529 while (i != _views.end() && i->view != _primary) {
2533 if (i == _views.end()) {
2537 switch (_operation) {
2539 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2542 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2549 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2553 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2554 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2559 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2561 Drag::start_grab (event, cursor);
2562 show_verbose_cursor_time (adjusted_current_frame(event));
2566 MeterMarkerDrag::setup_pointer_frame_offset ()
2568 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2572 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2574 if (!_marker->meter().movable()) {
2580 // create a dummy marker for visual representation of moving the
2581 // section, because whether its a copy or not, we're going to
2582 // leave or lose the original marker (leave if its a copy; lose if its
2583 // not, because we'll remove it from the map).
2585 MeterSection section (_marker->meter());
2587 if (!section.movable()) {
2592 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2594 _marker = new MeterMarker (
2596 *_editor->meter_group,
2597 ARDOUR_UI::config()->color ("meter marker"),
2599 *new MeterSection (_marker->meter())
2602 /* use the new marker for the grab */
2603 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2606 TempoMap& map (_editor->session()->tempo_map());
2607 /* get current state */
2608 before_state = &map.get_state();
2609 /* remove the section while we drag it */
2610 map.remove_meter (section, true);
2614 framepos_t const pf = adjusted_current_frame (event);
2616 _marker->set_position (pf);
2617 show_verbose_cursor_time (pf);
2621 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2623 if (!movement_occurred) {
2624 if (was_double_click()) {
2625 _editor->edit_meter_marker (*_marker);
2630 if (!_marker->meter().movable()) {
2634 motion (event, false);
2636 Timecode::BBT_Time when;
2638 TempoMap& map (_editor->session()->tempo_map());
2639 map.bbt_time (last_pointer_frame(), when);
2641 if (_copy == true) {
2642 _editor->begin_reversible_command (_("copy meter mark"));
2643 XMLNode &before = map.get_state();
2644 map.add_meter (_marker->meter(), when);
2645 XMLNode &after = map.get_state();
2646 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2647 _editor->commit_reversible_command ();
2650 _editor->begin_reversible_command (_("move meter mark"));
2652 /* we removed it before, so add it back now */
2654 map.add_meter (_marker->meter(), when);
2655 XMLNode &after = map.get_state();
2656 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2657 _editor->commit_reversible_command ();
2660 // delete the dummy marker we used for visual representation while moving.
2661 // a new visual marker will show up automatically.
2666 MeterMarkerDrag::aborted (bool moved)
2668 _marker->set_position (_marker->meter().frame ());
2671 TempoMap& map (_editor->session()->tempo_map());
2672 /* we removed it before, so add it back now */
2673 map.add_meter (_marker->meter(), _marker->meter().frame());
2674 // delete the dummy marker we used for visual representation while moving.
2675 // a new visual marker will show up automatically.
2680 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2684 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2686 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2691 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2693 Drag::start_grab (event, cursor);
2694 show_verbose_cursor_time (adjusted_current_frame (event));
2698 TempoMarkerDrag::setup_pointer_frame_offset ()
2700 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2704 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2706 if (!_marker->tempo().movable()) {
2712 // create a dummy marker for visual representation of moving the
2713 // section, because whether its a copy or not, we're going to
2714 // leave or lose the original marker (leave if its a copy; lose if its
2715 // not, because we'll remove it from the map).
2717 // create a dummy marker for visual representation of moving the copy.
2718 // The actual copying is not done before we reach the finish callback.
2721 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2723 TempoSection section (_marker->tempo());
2725 _marker = new TempoMarker (
2727 *_editor->tempo_group,
2728 ARDOUR_UI::config()->color ("tempo marker"),
2730 *new TempoSection (_marker->tempo())
2733 /* use the new marker for the grab */
2734 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2737 TempoMap& map (_editor->session()->tempo_map());
2738 /* get current state */
2739 before_state = &map.get_state();
2740 /* remove the section while we drag it */
2741 map.remove_tempo (section, true);
2745 framepos_t const pf = adjusted_current_frame (event);
2746 _marker->set_position (pf);
2747 show_verbose_cursor_time (pf);
2751 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2753 if (!movement_occurred) {
2754 if (was_double_click()) {
2755 _editor->edit_tempo_marker (*_marker);
2760 if (!_marker->tempo().movable()) {
2764 motion (event, false);
2766 TempoMap& map (_editor->session()->tempo_map());
2767 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
2768 Timecode::BBT_Time when;
2770 map.bbt_time (beat_time, when);
2772 if (_copy == true) {
2773 _editor->begin_reversible_command (_("copy tempo mark"));
2774 XMLNode &before = map.get_state();
2775 map.add_tempo (_marker->tempo(), when);
2776 XMLNode &after = map.get_state();
2777 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2778 _editor->commit_reversible_command ();
2781 _editor->begin_reversible_command (_("move tempo mark"));
2782 /* we removed it before, so add it back now */
2783 map.add_tempo (_marker->tempo(), when);
2784 XMLNode &after = map.get_state();
2785 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2786 _editor->commit_reversible_command ();
2789 // delete the dummy marker we used for visual representation while moving.
2790 // a new visual marker will show up automatically.
2795 TempoMarkerDrag::aborted (bool moved)
2797 _marker->set_position (_marker->tempo().frame());
2799 TempoMap& map (_editor->session()->tempo_map());
2800 /* we removed it before, so add it back now */
2801 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2802 // delete the dummy marker we used for visual representation while moving.
2803 // a new visual marker will show up automatically.
2808 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2809 : Drag (e, &c.track_canvas_item(), false)
2813 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2816 /** Do all the things we do when dragging the playhead to make it look as though
2817 * we have located, without actually doing the locate (because that would cause
2818 * the diskstream buffers to be refilled, which is too slow).
2821 CursorDrag::fake_locate (framepos_t t)
2823 _editor->playhead_cursor->set_position (t);
2825 Session* s = _editor->session ();
2826 if (s->timecode_transmission_suspended ()) {
2827 framepos_t const f = _editor->playhead_cursor->current_frame ();
2828 /* This is asynchronous so it will be sent "now"
2830 s->send_mmc_locate (f);
2831 /* These are synchronous and will be sent during the next
2834 s->queue_full_time_code ();
2835 s->queue_song_position_pointer ();
2838 show_verbose_cursor_time (t);
2839 _editor->UpdateAllTransportClocks (t);
2843 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2845 Drag::start_grab (event, c);
2847 _grab_zoom = _editor->samples_per_pixel;
2849 framepos_t where = _editor->canvas_event_sample (event);
2851 _editor->snap_to_with_modifier (where, event);
2853 _editor->_dragging_playhead = true;
2855 Session* s = _editor->session ();
2857 /* grab the track canvas item as well */
2859 _cursor.track_canvas_item().grab();
2862 if (_was_rolling && _stop) {
2866 if (s->is_auditioning()) {
2867 s->cancel_audition ();
2871 if (AudioEngine::instance()->connected()) {
2873 /* do this only if we're the engine is connected
2874 * because otherwise this request will never be
2875 * serviced and we'll busy wait forever. likewise,
2876 * notice if we are disconnected while waiting for the
2877 * request to be serviced.
2880 s->request_suspend_timecode_transmission ();
2881 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2882 /* twiddle our thumbs */
2887 fake_locate (where);
2891 CursorDrag::motion (GdkEvent* event, bool)
2893 framepos_t const adjusted_frame = adjusted_current_frame (event);
2894 if (adjusted_frame != last_pointer_frame()) {
2895 fake_locate (adjusted_frame);
2900 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2902 _editor->_dragging_playhead = false;
2904 _cursor.track_canvas_item().ungrab();
2906 if (!movement_occurred && _stop) {
2910 motion (event, false);
2912 Session* s = _editor->session ();
2914 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2915 _editor->_pending_locate_request = true;
2916 s->request_resume_timecode_transmission ();
2921 CursorDrag::aborted (bool)
2923 _cursor.track_canvas_item().ungrab();
2925 if (_editor->_dragging_playhead) {
2926 _editor->session()->request_resume_timecode_transmission ();
2927 _editor->_dragging_playhead = false;
2930 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2933 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2934 : RegionDrag (e, i, p, v)
2936 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2940 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2942 Drag::start_grab (event, cursor);
2944 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2945 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2947 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2951 FadeInDrag::setup_pointer_frame_offset ()
2953 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2954 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2955 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2959 FadeInDrag::motion (GdkEvent* event, bool)
2961 framecnt_t fade_length;
2963 framepos_t const pos = adjusted_current_frame (event);
2965 boost::shared_ptr<Region> region = _primary->region ();
2967 if (pos < (region->position() + 64)) {
2968 fade_length = 64; // this should be a minimum defined somewhere
2969 } else if (pos > region->last_frame()) {
2970 fade_length = region->length();
2972 fade_length = pos - region->position();
2975 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2977 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2983 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2986 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2990 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2992 if (!movement_occurred) {
2996 framecnt_t fade_length;
2998 framepos_t const pos = adjusted_current_frame (event);
3000 boost::shared_ptr<Region> region = _primary->region ();
3002 if (pos < (region->position() + 64)) {
3003 fade_length = 64; // this should be a minimum defined somewhere
3004 } else if (pos > region->last_frame()) {
3005 fade_length = region->length();
3007 fade_length = pos - region->position();
3010 _editor->begin_reversible_command (_("change fade in length"));
3012 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3014 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3020 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3021 XMLNode &before = alist->get_state();
3023 tmp->audio_region()->set_fade_in_length (fade_length);
3024 tmp->audio_region()->set_fade_in_active (true);
3026 XMLNode &after = alist->get_state();
3027 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3030 _editor->commit_reversible_command ();
3034 FadeInDrag::aborted (bool)
3036 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3037 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3043 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3047 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3048 : RegionDrag (e, i, p, v)
3050 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3054 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3056 Drag::start_grab (event, cursor);
3058 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3059 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3061 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3065 FadeOutDrag::setup_pointer_frame_offset ()
3067 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3068 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3069 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3073 FadeOutDrag::motion (GdkEvent* event, bool)
3075 framecnt_t fade_length;
3077 framepos_t const pos = adjusted_current_frame (event);
3079 boost::shared_ptr<Region> region = _primary->region ();
3081 if (pos > (region->last_frame() - 64)) {
3082 fade_length = 64; // this should really be a minimum fade defined somewhere
3084 else if (pos < region->position()) {
3085 fade_length = region->length();
3088 fade_length = region->last_frame() - pos;
3091 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3093 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3099 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3102 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3106 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3108 if (!movement_occurred) {
3112 framecnt_t fade_length;
3114 framepos_t const pos = adjusted_current_frame (event);
3116 boost::shared_ptr<Region> region = _primary->region ();
3118 if (pos > (region->last_frame() - 64)) {
3119 fade_length = 64; // this should really be a minimum fade defined somewhere
3121 else if (pos < region->position()) {
3122 fade_length = region->length();
3125 fade_length = region->last_frame() - pos;
3128 _editor->begin_reversible_command (_("change fade out length"));
3130 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3132 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3138 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3139 XMLNode &before = alist->get_state();
3141 tmp->audio_region()->set_fade_out_length (fade_length);
3142 tmp->audio_region()->set_fade_out_active (true);
3144 XMLNode &after = alist->get_state();
3145 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3148 _editor->commit_reversible_command ();
3152 FadeOutDrag::aborted (bool)
3154 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3155 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3161 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3165 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3168 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3170 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3173 _points.push_back (ArdourCanvas::Duple (0, 0));
3174 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3177 MarkerDrag::~MarkerDrag ()
3179 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3184 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3186 location = new Location (*l);
3187 markers.push_back (m);
3192 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3194 Drag::start_grab (event, cursor);
3198 Location *location = _editor->find_location_from_marker (_marker, is_start);
3199 _editor->_dragging_edit_point = true;
3201 update_item (location);
3203 // _drag_line->show();
3204 // _line->raise_to_top();
3207 show_verbose_cursor_time (location->start());
3209 show_verbose_cursor_time (location->end());
3212 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3215 case Selection::Toggle:
3216 /* we toggle on the button release */
3218 case Selection::Set:
3219 if (!_editor->selection->selected (_marker)) {
3220 _editor->selection->set (_marker);
3223 case Selection::Extend:
3225 Locations::LocationList ll;
3226 list<Marker*> to_add;
3228 _editor->selection->markers.range (s, e);
3229 s = min (_marker->position(), s);
3230 e = max (_marker->position(), e);
3233 if (e < max_framepos) {
3236 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3237 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3238 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3241 to_add.push_back (lm->start);
3244 to_add.push_back (lm->end);
3248 if (!to_add.empty()) {
3249 _editor->selection->add (to_add);
3253 case Selection::Add:
3254 _editor->selection->add (_marker);
3258 /* Set up copies for us to manipulate during the drag
3261 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3263 Location* l = _editor->find_location_from_marker (*i, is_start);
3270 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3272 /* range: check that the other end of the range isn't
3275 CopiedLocationInfo::iterator x;
3276 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3277 if (*(*x).location == *l) {
3281 if (x == _copied_locations.end()) {
3282 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3284 (*x).markers.push_back (*i);
3285 (*x).move_both = true;
3293 MarkerDrag::setup_pointer_frame_offset ()
3296 Location *location = _editor->find_location_from_marker (_marker, is_start);
3297 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3301 MarkerDrag::motion (GdkEvent* event, bool)
3303 framecnt_t f_delta = 0;
3305 bool move_both = false;
3306 Location *real_location;
3307 Location *copy_location = 0;
3309 framepos_t const newframe = adjusted_current_frame (event);
3310 framepos_t next = newframe;
3312 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3316 CopiedLocationInfo::iterator x;
3318 /* find the marker we're dragging, and compute the delta */
3320 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3322 copy_location = (*x).location;
3324 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3326 /* this marker is represented by this
3327 * CopiedLocationMarkerInfo
3330 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3335 if (real_location->is_mark()) {
3336 f_delta = newframe - copy_location->start();
3340 switch (_marker->type()) {
3341 case Marker::SessionStart:
3342 case Marker::RangeStart:
3343 case Marker::LoopStart:
3344 case Marker::PunchIn:
3345 f_delta = newframe - copy_location->start();
3348 case Marker::SessionEnd:
3349 case Marker::RangeEnd:
3350 case Marker::LoopEnd:
3351 case Marker::PunchOut:
3352 f_delta = newframe - copy_location->end();
3355 /* what kind of marker is this ? */
3364 if (x == _copied_locations.end()) {
3365 /* hmm, impossible - we didn't find the dragged marker */
3369 /* now move them all */
3371 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3373 copy_location = x->location;
3375 /* call this to find out if its the start or end */
3377 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3381 if (real_location->locked()) {
3385 if (copy_location->is_mark()) {
3389 copy_location->set_start (copy_location->start() + f_delta);
3393 framepos_t new_start = copy_location->start() + f_delta;
3394 framepos_t new_end = copy_location->end() + f_delta;
3396 if (is_start) { // start-of-range marker
3398 if (move_both || (*x).move_both) {
3399 copy_location->set_start (new_start);
3400 copy_location->set_end (new_end);
3401 } else if (new_start < copy_location->end()) {
3402 copy_location->set_start (new_start);
3403 } else if (newframe > 0) {
3404 _editor->snap_to (next, RoundUpAlways, true);
3405 copy_location->set_end (next);
3406 copy_location->set_start (newframe);
3409 } else { // end marker
3411 if (move_both || (*x).move_both) {
3412 copy_location->set_end (new_end);
3413 copy_location->set_start (new_start);
3414 } else if (new_end > copy_location->start()) {
3415 copy_location->set_end (new_end);
3416 } else if (newframe > 0) {
3417 _editor->snap_to (next, RoundDownAlways, true);
3418 copy_location->set_start (next);
3419 copy_location->set_end (newframe);
3424 update_item (copy_location);
3426 /* now lookup the actual GUI items used to display this
3427 * location and move them to wherever the copy of the location
3428 * is now. This means that the logic in ARDOUR::Location is
3429 * still enforced, even though we are not (yet) modifying
3430 * the real Location itself.
3433 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3436 lm->set_position (copy_location->start(), copy_location->end());
3441 assert (!_copied_locations.empty());
3443 show_verbose_cursor_time (newframe);
3447 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3449 if (!movement_occurred) {
3451 if (was_double_click()) {
3452 _editor->rename_marker (_marker);
3456 /* just a click, do nothing but finish
3457 off the selection process
3460 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3463 case Selection::Set:
3464 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3465 _editor->selection->set (_marker);
3469 case Selection::Toggle:
3470 /* we toggle on the button release, click only */
3471 _editor->selection->toggle (_marker);
3474 case Selection::Extend:
3475 case Selection::Add:
3482 _editor->_dragging_edit_point = false;
3484 _editor->begin_reversible_command ( _("move marker") );
3485 XMLNode &before = _editor->session()->locations()->get_state();
3487 MarkerSelection::iterator i;
3488 CopiedLocationInfo::iterator x;
3491 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3492 x != _copied_locations.end() && i != _editor->selection->markers.end();
3495 Location * location = _editor->find_location_from_marker (*i, is_start);
3499 if (location->locked()) {
3503 if (location->is_mark()) {
3504 location->set_start (((*x).location)->start());
3506 location->set (((*x).location)->start(), ((*x).location)->end());
3511 XMLNode &after = _editor->session()->locations()->get_state();
3512 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3513 _editor->commit_reversible_command ();
3517 MarkerDrag::aborted (bool)
3523 MarkerDrag::update_item (Location*)
3528 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3530 _cumulative_x_drag (0),
3531 _cumulative_y_drag (0)
3533 if (_zero_gain_fraction < 0.0) {
3534 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3537 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3539 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3545 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3547 Drag::start_grab (event, _editor->cursors()->fader);
3549 // start the grab at the center of the control point so
3550 // the point doesn't 'jump' to the mouse after the first drag
3551 _fixed_grab_x = _point->get_x();
3552 _fixed_grab_y = _point->get_y();
3554 float const fraction = 1 - (_point->get_y() / _point->line().height());
3556 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3558 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3560 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3562 if (!_point->can_slide ()) {
3563 _x_constrained = true;
3568 ControlPointDrag::motion (GdkEvent* event, bool)
3570 double dx = _drags->current_pointer_x() - last_pointer_x();
3571 double dy = current_pointer_y() - last_pointer_y();
3573 if (event->button.state & Keyboard::SecondaryModifier) {
3578 /* coordinate in pixels relative to the start of the region (for region-based automation)
3579 or track (for track-based automation) */
3580 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3581 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3583 // calculate zero crossing point. back off by .01 to stay on the
3584 // positive side of zero
3585 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3587 // make sure we hit zero when passing through
3588 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3592 if (_x_constrained) {
3595 if (_y_constrained) {
3599 _cumulative_x_drag = cx - _fixed_grab_x;
3600 _cumulative_y_drag = cy - _fixed_grab_y;
3604 cy = min ((double) _point->line().height(), cy);
3606 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3608 if (!_x_constrained) {
3609 _editor->snap_to_with_modifier (cx_frames, event);
3612 cx_frames = min (cx_frames, _point->line().maximum_time());
3614 float const fraction = 1.0 - (cy / _point->line().height());
3616 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3618 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3622 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3624 if (!movement_occurred) {
3628 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3629 _editor->reset_point_selection ();
3633 motion (event, false);
3636 _point->line().end_drag (_pushing, _final_index);
3637 _editor->commit_reversible_command ();
3641 ControlPointDrag::aborted (bool)
3643 _point->line().reset ();
3647 ControlPointDrag::active (Editing::MouseMode m)
3649 if (m == Editing::MouseDraw) {
3650 /* always active in mouse draw */
3654 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3655 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3658 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3661 _cumulative_y_drag (0)
3663 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3667 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3669 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3672 _item = &_line->grab_item ();
3674 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3675 origin, and ditto for y.
3678 double cx = event->button.x;
3679 double cy = event->button.y;
3681 _line->parent_group().canvas_to_item (cx, cy);
3683 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3688 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3689 /* no adjacent points */
3693 Drag::start_grab (event, _editor->cursors()->fader);
3695 /* store grab start in parent frame */
3700 double fraction = 1.0 - (cy / _line->height());
3702 _line->start_drag_line (before, after, fraction);
3704 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3708 LineDrag::motion (GdkEvent* event, bool)
3710 double dy = current_pointer_y() - last_pointer_y();
3712 if (event->button.state & Keyboard::SecondaryModifier) {
3716 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3718 _cumulative_y_drag = cy - _fixed_grab_y;
3721 cy = min ((double) _line->height(), cy);
3723 double const fraction = 1.0 - (cy / _line->height());
3726 /* we are ignoring x position for this drag, so we can just pass in anything */
3727 _line->drag_motion (0, fraction, true, false, ignored);
3729 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3733 LineDrag::finished (GdkEvent* event, bool movement_occured)
3735 if (movement_occured) {
3736 motion (event, false);
3737 _line->end_drag (false, 0);
3739 /* add a new control point on the line */
3741 AutomationTimeAxisView* atv;
3743 _line->end_drag (false, 0);
3745 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3746 framepos_t where = _editor->window_event_sample (event, 0, 0);
3747 atv->add_automation_event (event, where, event->button.y, false);
3751 _editor->commit_reversible_command ();
3755 LineDrag::aborted (bool)
3760 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3763 _cumulative_x_drag (0)
3765 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3769 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3771 Drag::start_grab (event);
3773 _line = reinterpret_cast<Line*> (_item);
3776 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3778 double cx = event->button.x;
3779 double cy = event->button.y;
3781 _item->parent()->canvas_to_item (cx, cy);
3783 /* store grab start in parent frame */
3784 _region_view_grab_x = cx;
3786 _before = *(float*) _item->get_data ("position");
3788 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3790 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3794 FeatureLineDrag::motion (GdkEvent*, bool)
3796 double dx = _drags->current_pointer_x() - last_pointer_x();
3798 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3800 _cumulative_x_drag += dx;
3802 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3811 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3813 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3815 float *pos = new float;
3818 _line->set_data ("position", pos);
3824 FeatureLineDrag::finished (GdkEvent*, bool)
3826 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3827 _arv->update_transient(_before, _before);
3831 FeatureLineDrag::aborted (bool)
3836 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3838 , _vertical_only (false)
3840 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3844 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3846 Drag::start_grab (event);
3847 show_verbose_cursor_time (adjusted_current_frame (event));
3851 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3858 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3860 framepos_t grab = grab_frame ();
3861 if (Config->get_rubberbanding_snaps_to_grid ()) {
3862 _editor->snap_to_with_modifier (grab, event);
3865 /* base start and end on initial click position */
3875 if (current_pointer_y() < grab_y()) {
3876 y1 = current_pointer_y();
3879 y2 = current_pointer_y();
3883 if (start != end || y1 != y2) {
3885 double x1 = _editor->sample_to_pixel (start);
3886 double x2 = _editor->sample_to_pixel (end);
3887 const double min_dimension = 2.0;
3889 if (_vertical_only) {
3890 /* fixed 10 pixel width */
3894 x2 = min (x1 - min_dimension, x2);
3896 x2 = max (x1 + min_dimension, x2);
3901 y2 = min (y1 - min_dimension, y2);
3903 y2 = max (y1 + min_dimension, y2);
3906 /* translate rect into item space and set */
3908 ArdourCanvas::Rect r (x1, y1, x2, y2);
3910 /* this drag is a _trackview_only == true drag, so the y1 and
3911 * y2 (computed using current_pointer_y() and grab_y()) will be
3912 * relative to the top of the trackview group). The
3913 * rubberband rect has the same parent/scroll offset as the
3914 * the trackview group, so we can use the "r" rect directly
3915 * to set the shape of the rubberband.
3918 _editor->rubberband_rect->set (r);
3919 _editor->rubberband_rect->show();
3920 _editor->rubberband_rect->raise_to_top();
3922 show_verbose_cursor_time (pf);
3924 do_select_things (event, true);
3929 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3934 if (grab_frame() < last_pointer_frame()) {
3936 x2 = last_pointer_frame ();
3939 x1 = last_pointer_frame ();
3945 if (current_pointer_y() < grab_y()) {
3946 y1 = current_pointer_y();
3949 y2 = current_pointer_y();
3953 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3957 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3959 if (movement_occurred) {
3961 motion (event, false);
3962 do_select_things (event, false);
3968 bool do_deselect = true;
3969 MidiTimeAxisView* mtv;
3971 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3973 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
3974 /* nothing selected */
3975 add_midi_region (mtv);
3976 do_deselect = false;
3980 /* do not deselect if Primary or Tertiary (toggle-select or
3981 * extend-select are pressed.
3984 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3985 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3992 _editor->rubberband_rect->hide();
3996 RubberbandSelectDrag::aborted (bool)
3998 _editor->rubberband_rect->hide ();
4001 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4002 : RegionDrag (e, i, p, v)
4004 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4008 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4010 Drag::start_grab (event, cursor);
4012 show_verbose_cursor_time (adjusted_current_frame (event));
4016 TimeFXDrag::motion (GdkEvent* event, bool)
4018 RegionView* rv = _primary;
4019 StreamView* cv = rv->get_time_axis_view().view ();
4021 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4022 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4023 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4025 framepos_t const pf = adjusted_current_frame (event);
4027 if (pf > rv->region()->position()) {
4028 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4031 show_verbose_cursor_time (pf);
4035 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4037 _primary->get_time_axis_view().hide_timestretch ();
4039 if (!movement_occurred) {
4043 if (last_pointer_frame() < _primary->region()->position()) {
4044 /* backwards drag of the left edge - not usable */
4048 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4050 float percentage = (double) newlen / (double) _primary->region()->length();
4052 #ifndef USE_RUBBERBAND
4053 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4054 if (_primary->region()->data_type() == DataType::AUDIO) {
4055 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4059 if (!_editor->get_selection().regions.empty()) {
4060 /* primary will already be included in the selection, and edit
4061 group shared editing will propagate selection across
4062 equivalent regions, so just use the current region
4066 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4067 error << _("An error occurred while executing time stretch operation") << endmsg;
4073 TimeFXDrag::aborted (bool)
4075 _primary->get_time_axis_view().hide_timestretch ();
4078 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4081 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4085 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4087 Drag::start_grab (event);
4091 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4093 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4097 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4099 if (movement_occurred && _editor->session()) {
4100 /* make sure we stop */
4101 _editor->session()->request_transport_speed (0.0);
4106 ScrubDrag::aborted (bool)
4111 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4115 , _original_pointer_time_axis (-1)
4116 , _time_selection_at_start (!_editor->get_selection().time.empty())
4118 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4120 if (_time_selection_at_start) {
4121 start_at_start = _editor->get_selection().time.start();
4122 end_at_start = _editor->get_selection().time.end_frame();
4127 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4129 if (_editor->session() == 0) {
4133 Gdk::Cursor* cursor = 0;
4135 switch (_operation) {
4136 case CreateSelection:
4137 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4142 cursor = _editor->cursors()->selector;
4143 Drag::start_grab (event, cursor);
4146 case SelectionStartTrim:
4147 if (_editor->clicked_axisview) {
4148 _editor->clicked_axisview->order_selection_trims (_item, true);
4150 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4153 case SelectionEndTrim:
4154 if (_editor->clicked_axisview) {
4155 _editor->clicked_axisview->order_selection_trims (_item, false);
4157 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4161 Drag::start_grab (event, cursor);
4164 case SelectionExtend:
4165 Drag::start_grab (event, cursor);
4169 if (_operation == SelectionMove) {
4170 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4172 show_verbose_cursor_time (adjusted_current_frame (event));
4175 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4179 SelectionDrag::setup_pointer_frame_offset ()
4181 switch (_operation) {
4182 case CreateSelection:
4183 _pointer_frame_offset = 0;
4186 case SelectionStartTrim:
4188 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4191 case SelectionEndTrim:
4192 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4195 case SelectionExtend:
4201 SelectionDrag::motion (GdkEvent* event, bool first_move)
4203 framepos_t start = 0;
4205 framecnt_t length = 0;
4206 framecnt_t distance = 0;
4208 framepos_t const pending_position = adjusted_current_frame (event);
4210 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4214 switch (_operation) {
4215 case CreateSelection:
4217 framepos_t grab = grab_frame ();
4220 grab = adjusted_current_frame (event, false);
4221 if (grab < pending_position) {
4222 _editor->snap_to (grab, RoundDownMaybe);
4224 _editor->snap_to (grab, RoundUpMaybe);
4228 if (pending_position < grab) {
4229 start = pending_position;
4232 end = pending_position;
4236 /* first drag: Either add to the selection
4237 or create a new selection
4244 /* adding to the selection */
4245 _editor->set_selected_track_as_side_effect (Selection::Add);
4246 _editor->clicked_selection = _editor->selection->add (start, end);
4253 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4254 _editor->set_selected_track_as_side_effect (Selection::Set);
4257 _editor->clicked_selection = _editor->selection->set (start, end);
4261 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4262 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4263 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4265 _editor->selection->add (atest);
4269 /* select all tracks within the rectangle that we've marked out so far */
4270 TrackViewList new_selection;
4271 TrackViewList& all_tracks (_editor->track_views);
4273 ArdourCanvas::Coord const top = grab_y();
4274 ArdourCanvas::Coord const bottom = current_pointer_y();
4276 if (top >= 0 && bottom >= 0) {
4278 //first, find the tracks that are covered in the y range selection
4279 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4280 if ((*i)->covered_by_y_range (top, bottom)) {
4281 new_selection.push_back (*i);
4285 //now find any tracks that are GROUPED with the tracks we selected
4286 TrackViewList grouped_add = new_selection;
4287 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4288 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4289 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4290 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4291 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4292 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4293 grouped_add.push_back (*j);
4298 //now compare our list with the current selection, and add or remove as necessary
4299 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4300 TrackViewList tracks_to_add;
4301 TrackViewList tracks_to_remove;
4302 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4303 if ( !_editor->selection->tracks.contains ( *i ) )
4304 tracks_to_add.push_back ( *i );
4305 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4306 if ( !grouped_add.contains ( *i ) )
4307 tracks_to_remove.push_back ( *i );
4308 _editor->selection->add(tracks_to_add);
4309 _editor->selection->remove(tracks_to_remove);
4315 case SelectionStartTrim:
4317 start = _editor->selection->time[_editor->clicked_selection].start;
4318 end = _editor->selection->time[_editor->clicked_selection].end;
4320 if (pending_position > end) {
4323 start = pending_position;
4327 case SelectionEndTrim:
4329 start = _editor->selection->time[_editor->clicked_selection].start;
4330 end = _editor->selection->time[_editor->clicked_selection].end;
4332 if (pending_position < start) {
4335 end = pending_position;
4342 start = _editor->selection->time[_editor->clicked_selection].start;
4343 end = _editor->selection->time[_editor->clicked_selection].end;
4345 length = end - start;
4346 distance = pending_position - start;
4347 start = pending_position;
4348 _editor->snap_to (start);
4350 end = start + length;
4354 case SelectionExtend:
4359 switch (_operation) {
4361 if (_time_selection_at_start) {
4362 _editor->selection->move_time (distance);
4366 _editor->selection->replace (_editor->clicked_selection, start, end);
4370 if (_operation == SelectionMove) {
4371 show_verbose_cursor_time(start);
4373 show_verbose_cursor_time(pending_position);
4378 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4380 Session* s = _editor->session();
4382 if (movement_occurred) {
4383 motion (event, false);
4384 /* XXX this is not object-oriented programming at all. ick */
4385 if (_editor->selection->time.consolidate()) {
4386 _editor->selection->TimeChanged ();
4389 /* XXX what if its a music time selection? */
4391 if ( s->get_play_range() && s->transport_rolling() ) {
4392 s->request_play_range (&_editor->selection->time, true);
4394 if (Config->get_follow_edits() && !s->transport_rolling()) {
4395 if (_operation == SelectionEndTrim)
4396 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4398 s->request_locate (_editor->get_selection().time.start());
4404 /* just a click, no pointer movement.
4407 if (_operation == SelectionExtend) {
4408 if (_time_selection_at_start) {
4409 framepos_t pos = adjusted_current_frame (event, false);
4410 framepos_t start = min (pos, start_at_start);
4411 framepos_t end = max (pos, end_at_start);
4412 _editor->selection->set (start, end);
4415 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4416 if (_editor->clicked_selection) {
4417 _editor->selection->remove (_editor->clicked_selection);
4420 if (!_editor->clicked_selection) {
4421 _editor->selection->clear_time();
4426 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4427 _editor->selection->set (_editor->clicked_axisview);
4430 if (s && s->get_play_range () && s->transport_rolling()) {
4431 s->request_stop (false, false);
4436 _editor->stop_canvas_autoscroll ();
4437 _editor->clicked_selection = 0;
4441 SelectionDrag::aborted (bool)
4446 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4447 : Drag (e, i, false),
4451 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4453 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4454 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4455 physical_screen_height (_editor->get_window())));
4456 _drag_rect->hide ();
4458 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4459 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4463 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4465 if (_editor->session() == 0) {
4469 Gdk::Cursor* cursor = 0;
4471 if (!_editor->temp_location) {
4472 _editor->temp_location = new Location (*_editor->session());
4475 switch (_operation) {
4476 case CreateSkipMarker:
4477 case CreateRangeMarker:
4478 case CreateTransportMarker:
4479 case CreateCDMarker:
4481 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4486 cursor = _editor->cursors()->selector;
4490 Drag::start_grab (event, cursor);
4492 show_verbose_cursor_time (adjusted_current_frame (event));
4496 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4498 framepos_t start = 0;
4500 ArdourCanvas::Rectangle *crect;
4502 switch (_operation) {
4503 case CreateSkipMarker:
4504 crect = _editor->range_bar_drag_rect;
4506 case CreateRangeMarker:
4507 crect = _editor->range_bar_drag_rect;
4509 case CreateTransportMarker:
4510 crect = _editor->transport_bar_drag_rect;
4512 case CreateCDMarker:
4513 crect = _editor->cd_marker_bar_drag_rect;
4516 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4521 framepos_t const pf = adjusted_current_frame (event);
4523 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4524 framepos_t grab = grab_frame ();
4525 _editor->snap_to (grab);
4527 if (pf < grab_frame()) {
4535 /* first drag: Either add to the selection
4536 or create a new selection.
4541 _editor->temp_location->set (start, end);
4545 update_item (_editor->temp_location);
4547 //_drag_rect->raise_to_top();
4553 _editor->temp_location->set (start, end);
4555 double x1 = _editor->sample_to_pixel (start);
4556 double x2 = _editor->sample_to_pixel (end);
4560 update_item (_editor->temp_location);
4563 show_verbose_cursor_time (pf);
4568 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4570 Location * newloc = 0;
4574 if (movement_occurred) {
4575 motion (event, false);
4578 switch (_operation) {
4579 case CreateSkipMarker:
4580 case CreateRangeMarker:
4581 case CreateCDMarker:
4583 XMLNode &before = _editor->session()->locations()->get_state();
4584 if (_operation == CreateSkipMarker) {
4585 _editor->begin_reversible_command (_("new skip marker"));
4586 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4587 flags = Location::IsRangeMarker | Location::IsSkip;
4588 _editor->range_bar_drag_rect->hide();
4589 } else if (_operation == CreateCDMarker) {
4590 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4591 _editor->begin_reversible_command (_("new CD marker"));
4592 flags = Location::IsRangeMarker | Location::IsCDMarker;
4593 _editor->cd_marker_bar_drag_rect->hide();
4595 _editor->begin_reversible_command (_("new skip marker"));
4596 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4597 flags = Location::IsRangeMarker;
4598 _editor->range_bar_drag_rect->hide();
4600 newloc = new Location (
4601 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4604 _editor->session()->locations()->add (newloc, true);
4605 XMLNode &after = _editor->session()->locations()->get_state();
4606 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4607 _editor->commit_reversible_command ();
4611 case CreateTransportMarker:
4612 // popup menu to pick loop or punch
4613 _editor->new_transport_marker_context_menu (&event->button, _item);
4619 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4621 if (_operation == CreateTransportMarker) {
4623 /* didn't drag, so just locate */
4625 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4627 } else if (_operation == CreateCDMarker) {
4629 /* didn't drag, but mark is already created so do
4632 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
4638 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4640 if (end == max_framepos) {
4641 end = _editor->session()->current_end_frame ();
4644 if (start == max_framepos) {
4645 start = _editor->session()->current_start_frame ();
4648 switch (_editor->mouse_mode) {
4650 /* find the two markers on either side and then make the selection from it */
4651 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4655 /* find the two markers on either side of the click and make the range out of it */
4656 _editor->selection->set (start, end);
4665 _editor->stop_canvas_autoscroll ();
4669 RangeMarkerBarDrag::aborted (bool)
4675 RangeMarkerBarDrag::update_item (Location* location)
4677 double const x1 = _editor->sample_to_pixel (location->start());
4678 double const x2 = _editor->sample_to_pixel (location->end());
4680 _drag_rect->set_x0 (x1);
4681 _drag_rect->set_x1 (x2);
4684 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4686 , _cumulative_dx (0)
4687 , _cumulative_dy (0)
4689 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4691 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4693 _region = &_primary->region_view ();
4694 _note_height = _region->midi_stream_view()->note_height ();
4698 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4700 Drag::start_grab (event);
4702 if (!(_was_selected = _primary->selected())) {
4704 /* tertiary-click means extend selection - we'll do that on button release,
4705 so don't add it here, because otherwise we make it hard to figure
4706 out the "extend-to" range.
4709 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4712 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4715 _region->note_selected (_primary, true);
4717 _region->unique_select (_primary);
4723 /** @return Current total drag x change in frames */
4725 NoteDrag::total_dx () const
4728 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4730 /* primary note time */
4731 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4733 /* new time of the primary note in session frames */
4734 frameoffset_t st = n + dx;
4736 framepos_t const rp = _region->region()->position ();
4738 /* prevent the note being dragged earlier than the region's position */
4741 /* snap and return corresponding delta */
4742 return _region->snap_frame_to_frame (st - rp) + rp - n;
4745 /** @return Current total drag y change in note number */
4747 NoteDrag::total_dy () const
4749 MidiStreamView* msv = _region->midi_stream_view ();
4750 double const y = _region->midi_view()->y_position ();
4751 /* new current note */
4752 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4754 n = max (msv->lowest_note(), n);
4755 n = min (msv->highest_note(), n);
4756 /* and work out delta */
4757 return n - msv->y_to_note (grab_y() - y);
4761 NoteDrag::motion (GdkEvent *, bool)
4763 /* Total change in x and y since the start of the drag */
4764 frameoffset_t const dx = total_dx ();
4765 int8_t const dy = total_dy ();
4767 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4768 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4769 double const tdy = -dy * _note_height - _cumulative_dy;
4772 _cumulative_dx += tdx;
4773 _cumulative_dy += tdy;
4775 int8_t note_delta = total_dy();
4777 _region->move_selection (tdx, tdy, note_delta);
4779 /* the new note value may be the same as the old one, but we
4780 * don't know what that means because the selection may have
4781 * involved more than one note and we might be doing something
4782 * odd with them. so show the note value anyway, always.
4786 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4788 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4789 (int) floor ((double)new_note));
4791 show_verbose_cursor_text (buf);
4796 NoteDrag::finished (GdkEvent* ev, bool moved)
4799 /* no motion - select note */
4801 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4802 _editor->current_mouse_mode() == Editing::MouseDraw) {
4804 if (_was_selected) {
4805 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4807 _region->note_deselected (_primary);
4810 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4811 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4813 if (!extend && !add && _region->selection_size() > 1) {
4814 _region->unique_select (_primary);
4815 } else if (extend) {
4816 _region->note_selected (_primary, true, true);
4818 /* it was added during button press */
4823 _region->note_dropped (_primary, total_dx(), total_dy());
4828 NoteDrag::aborted (bool)
4833 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4834 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4835 : Drag (editor, atv->base_item ())
4837 , _nothing_to_drag (false)
4839 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4840 y_origin = atv->y_position();
4841 setup (atv->lines ());
4844 /** Make an AutomationRangeDrag for region gain lines */
4845 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4846 : Drag (editor, rv->get_canvas_group ())
4848 , _nothing_to_drag (false)
4850 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4852 list<boost::shared_ptr<AutomationLine> > lines;
4853 lines.push_back (rv->get_gain_line ());
4854 y_origin = rv->get_time_axis_view().y_position();
4858 /** @param lines AutomationLines to drag.
4859 * @param offset Offset from the session start to the points in the AutomationLines.
4862 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4864 /* find the lines that overlap the ranges being dragged */
4865 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4866 while (i != lines.end ()) {
4867 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4870 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4872 /* check this range against all the AudioRanges that we are using */
4873 list<AudioRange>::const_iterator k = _ranges.begin ();
4874 while (k != _ranges.end()) {
4875 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4881 /* add it to our list if it overlaps at all */
4882 if (k != _ranges.end()) {
4887 _lines.push_back (n);
4893 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4897 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4899 return 1.0 - ((global_y - y_origin) / line->height());
4903 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4905 Drag::start_grab (event, cursor);
4907 /* Get line states before we start changing things */
4908 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4909 i->state = &i->line->get_state ();
4910 i->original_fraction = y_fraction (i->line, current_pointer_y());
4913 if (_ranges.empty()) {
4915 /* No selected time ranges: drag all points */
4916 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4917 uint32_t const N = i->line->npoints ();
4918 for (uint32_t j = 0; j < N; ++j) {
4919 i->points.push_back (i->line->nth (j));
4925 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4927 framecnt_t const half = (i->start + i->end) / 2;
4929 /* find the line that this audio range starts in */
4930 list<Line>::iterator j = _lines.begin();
4931 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4935 if (j != _lines.end()) {
4936 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4938 /* j is the line that this audio range starts in; fade into it;
4939 64 samples length plucked out of thin air.
4942 framepos_t a = i->start + 64;
4947 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4948 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4950 the_list->editor_add (p, the_list->eval (p));
4951 the_list->editor_add (q, the_list->eval (q));
4954 /* same thing for the end */
4957 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4961 if (j != _lines.end()) {
4962 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4964 /* j is the line that this audio range starts in; fade out of it;
4965 64 samples length plucked out of thin air.
4968 framepos_t b = i->end - 64;
4973 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4974 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4976 the_list->editor_add (p, the_list->eval (p));
4977 the_list->editor_add (q, the_list->eval (q));
4981 _nothing_to_drag = true;
4983 /* Find all the points that should be dragged and put them in the relevant
4984 points lists in the Line structs.
4987 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4989 uint32_t const N = i->line->npoints ();
4990 for (uint32_t j = 0; j < N; ++j) {
4992 /* here's a control point on this line */
4993 ControlPoint* p = i->line->nth (j);
4994 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4996 /* see if it's inside a range */
4997 list<AudioRange>::const_iterator k = _ranges.begin ();
4998 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5002 if (k != _ranges.end()) {
5003 /* dragging this point */
5004 _nothing_to_drag = false;
5005 i->points.push_back (p);
5011 if (_nothing_to_drag) {
5015 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5016 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5021 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5023 if (_nothing_to_drag) {
5027 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5028 float const f = y_fraction (l->line, current_pointer_y());
5029 /* we are ignoring x position for this drag, so we can just pass in anything */
5031 l->line->drag_motion (0, f, true, false, ignored);
5032 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5037 AutomationRangeDrag::finished (GdkEvent* event, bool)
5039 if (_nothing_to_drag) {
5043 motion (event, false);
5044 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5045 i->line->end_drag (false, 0);
5048 _editor->commit_reversible_command ();
5052 AutomationRangeDrag::aborted (bool)
5054 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5059 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5061 , initial_time_axis_view (itav)
5063 /* note that time_axis_view may be null if the regionview was created
5064 * as part of a copy operation.
5066 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5067 layer = v->region()->layer ();
5068 initial_y = v->get_canvas_group()->position().y;
5069 initial_playlist = v->region()->playlist ();
5070 initial_position = v->region()->position ();
5071 initial_end = v->region()->position () + v->region()->length ();
5074 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5075 : Drag (e, i->canvas_item ())
5078 , _cumulative_dx (0)
5080 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5081 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5086 PatchChangeDrag::motion (GdkEvent* ev, bool)
5088 framepos_t f = adjusted_current_frame (ev);
5089 boost::shared_ptr<Region> r = _region_view->region ();
5090 f = max (f, r->position ());
5091 f = min (f, r->last_frame ());
5093 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5094 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5095 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5096 _cumulative_dx = dxu;
5100 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5102 if (!movement_occurred) {
5106 boost::shared_ptr<Region> r (_region_view->region ());
5107 framepos_t f = adjusted_current_frame (ev);
5108 f = max (f, r->position ());
5109 f = min (f, r->last_frame ());
5111 _region_view->move_patch_change (
5113 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5118 PatchChangeDrag::aborted (bool)
5120 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5124 PatchChangeDrag::setup_pointer_frame_offset ()
5126 boost::shared_ptr<Region> region = _region_view->region ();
5127 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5130 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5131 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5138 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5140 _region_view->update_drag_selection (
5142 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5146 MidiRubberbandSelectDrag::deselect_things ()
5151 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5152 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5155 _vertical_only = true;
5159 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5161 double const y = _region_view->midi_view()->y_position ();
5163 y1 = max (0.0, y1 - y);
5164 y2 = max (0.0, y2 - y);
5166 _region_view->update_vertical_drag_selection (
5169 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5174 MidiVerticalSelectDrag::deselect_things ()
5179 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5180 : RubberbandSelectDrag (e, i)
5186 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5188 if (drag_in_progress) {
5189 /* We just want to select things at the end of the drag, not during it */
5193 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5195 _editor->begin_reversible_command (_("rubberband selection"));
5197 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5199 _editor->commit_reversible_command ();
5203 EditorRubberbandSelectDrag::deselect_things ()
5205 if (!getenv("ARDOUR_SAE")) {
5206 _editor->selection->clear_tracks();
5208 _editor->selection->clear_regions();
5209 _editor->selection->clear_points ();
5210 _editor->selection->clear_lines ();
5213 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5218 _note[0] = _note[1] = 0;
5221 NoteCreateDrag::~NoteCreateDrag ()
5227 NoteCreateDrag::grid_frames (framepos_t t) const
5230 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
5232 grid_beats = Evoral::MusicalTime(1);
5235 return _region_view->region_beats_to_region_frames (grid_beats);
5239 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5241 Drag::start_grab (event, cursor);
5243 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5245 framepos_t pf = _drags->current_pointer_frame ();
5246 framecnt_t const g = grid_frames (pf);
5248 /* Hack so that we always snap to the note that we are over, instead of snapping
5249 to the next one if we're more than halfway through the one we're over.
5251 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5255 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5256 _note[1] = _note[0];
5258 MidiStreamView* sv = _region_view->midi_stream_view ();
5259 double const x = _editor->sample_to_pixel (_note[0]);
5260 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5262 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5263 _drag_rect->set_outline_all ();
5264 _drag_rect->set_outline_color (0xffffff99);
5265 _drag_rect->set_fill_color (0xffffff66);
5269 NoteCreateDrag::motion (GdkEvent* event, bool)
5271 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5272 double const x0 = _editor->sample_to_pixel (_note[0]);
5273 double const x1 = _editor->sample_to_pixel (_note[1]);
5274 _drag_rect->set_x0 (std::min(x0, x1));
5275 _drag_rect->set_x1 (std::max(x0, x1));
5279 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5281 if (!had_movement) {
5285 framepos_t const start = min (_note[0], _note[1]);
5286 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5288 framecnt_t const g = grid_frames (start);
5289 Evoral::MusicalTime const one_tick = Evoral::MusicalTime::ticks(1);
5291 if (_editor->snap_mode() == SnapNormal && length < g) {
5295 Evoral::MusicalTime length_beats = max (
5296 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5298 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5302 NoteCreateDrag::y_to_region (double y) const
5305 _region_view->get_canvas_group()->canvas_to_item (x, y);
5310 NoteCreateDrag::aborted (bool)
5315 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5320 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5324 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5326 Drag::start_grab (event, cursor);
5330 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5336 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5339 distance = _drags->current_pointer_x() - grab_x();
5340 len = ar->fade_in()->back()->when;
5342 distance = grab_x() - _drags->current_pointer_x();
5343 len = ar->fade_out()->back()->when;
5346 /* how long should it be ? */
5348 new_length = len + _editor->pixel_to_sample (distance);
5350 /* now check with the region that this is legal */
5352 new_length = ar->verify_xfade_bounds (new_length, start);
5355 arv->reset_fade_in_shape_width (ar, new_length);
5357 arv->reset_fade_out_shape_width (ar, new_length);
5362 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5368 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5371 distance = _drags->current_pointer_x() - grab_x();
5372 len = ar->fade_in()->back()->when;
5374 distance = grab_x() - _drags->current_pointer_x();
5375 len = ar->fade_out()->back()->when;
5378 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5380 _editor->begin_reversible_command ("xfade trim");
5381 ar->playlist()->clear_owned_changes ();
5384 ar->set_fade_in_length (new_length);
5386 ar->set_fade_out_length (new_length);
5389 /* Adjusting the xfade may affect other regions in the playlist, so we need
5390 to get undo Commands from the whole playlist rather than just the
5394 vector<Command*> cmds;
5395 ar->playlist()->rdiff (cmds);
5396 _editor->session()->add_commands (cmds);
5397 _editor->commit_reversible_command ();
5402 CrossfadeEdgeDrag::aborted (bool)
5405 arv->redraw_start_xfade ();
5407 arv->redraw_end_xfade ();
5411 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5412 : Drag (e, item, true)
5413 , line (new EditorCursor (*e))
5415 line->set_position (pos);
5419 RegionCutDrag::~RegionCutDrag ()
5425 RegionCutDrag::motion (GdkEvent*, bool)
5427 framepos_t where = _drags->current_pointer_frame();
5428 _editor->snap_to (where);
5430 line->set_position (where);
5434 RegionCutDrag::finished (GdkEvent*, bool)
5436 _editor->get_track_canvas()->canvas()->re_enter();
5438 framepos_t pos = _drags->current_pointer_frame();
5442 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5448 _editor->split_regions_at (pos, rs);
5452 RegionCutDrag::aborted (bool)