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 "automation_region_view.h"
51 #include "midi_region_view.h"
52 #include "ardour_ui.h"
53 #include "gui_thread.h"
54 #include "control_point.h"
55 #include "region_gain_line.h"
56 #include "editor_drag.h"
57 #include "audio_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "selection.h"
60 #include "midi_selection.h"
61 #include "automation_time_axis.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "verbose_cursor.h"
70 using namespace ARDOUR;
73 using namespace Gtkmm2ext;
74 using namespace Editing;
75 using namespace ArdourCanvas;
77 using Gtkmm2ext::Keyboard;
79 double ControlPointDrag::_zero_gain_fraction = -1.0;
81 DragManager::DragManager (Editor* e)
84 , _current_pointer_frame (0)
88 DragManager::~DragManager ()
93 /** Call abort for each active drag */
99 cerr << "Aborting drag\n";
101 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
106 if (!_drags.empty ()) {
107 _editor->set_follow_playhead (_old_follow_playhead, false);
111 _editor->abort_reversible_command();
117 DragManager::add (Drag* d)
119 d->set_manager (this);
120 _drags.push_back (d);
124 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
126 d->set_manager (this);
127 _drags.push_back (d);
132 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
134 /* Prevent follow playhead during the drag to be nice to the user */
135 _old_follow_playhead = _editor->follow_playhead ();
136 _editor->set_follow_playhead (false);
138 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
140 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
141 (*i)->start_grab (e, c);
145 /** Call end_grab for each active drag.
146 * @return true if any drag reported movement having occurred.
149 DragManager::end_grab (GdkEvent* e)
154 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
155 bool const t = (*i)->end_grab (e);
166 _editor->set_follow_playhead (_old_follow_playhead, false);
172 DragManager::mark_double_click ()
174 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
175 (*i)->set_double_click (true);
180 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
184 /* calling this implies that we expect the event to have canvas
187 * Can we guarantee that this is true?
190 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
192 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
193 bool const t = (*i)->motion_handler (e, from_autoscroll);
194 /* run all handlers; return true if at least one of them
195 returns true (indicating that the event has been handled).
207 DragManager::have_item (ArdourCanvas::Item* i) const
209 list<Drag*>::const_iterator j = _drags.begin ();
210 while (j != _drags.end() && (*j)->item () != i) {
214 return j != _drags.end ();
217 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
220 , _pointer_frame_offset (0)
221 , _trackview_only (trackview_only)
222 , _move_threshold_passed (false)
223 , _starting_point_passed (false)
224 , _was_double_click (false)
225 , _raw_grab_frame (0)
227 , _last_pointer_frame (0)
233 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
239 _cursor_ctx = CursorContext::create (*_editor, cursor);
241 _cursor_ctx->change (cursor);
248 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
250 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
252 if (Keyboard::is_button2_event (&event->button)) {
253 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
254 _y_constrained = true;
255 _x_constrained = false;
257 _y_constrained = false;
258 _x_constrained = true;
261 _x_constrained = false;
262 _y_constrained = false;
265 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
266 setup_pointer_frame_offset ();
267 _grab_frame = adjusted_frame (_raw_grab_frame, event);
268 _last_pointer_frame = _grab_frame;
269 _last_pointer_x = _grab_x;
271 if (_trackview_only) {
272 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
275 _last_pointer_y = _grab_y;
279 if (!_editor->cursors()->is_invalid (cursor)) {
280 /* CAIROCANVAS need a variant here that passes *cursor */
281 _cursor_ctx = CursorContext::create (*_editor, cursor);
284 if (_editor->session() && _editor->session()->transport_rolling()) {
287 _was_rolling = false;
290 switch (_editor->snap_type()) {
291 case SnapToRegionStart:
292 case SnapToRegionEnd:
293 case SnapToRegionSync:
294 case SnapToRegionBoundary:
295 _editor->build_region_boundary_cache ();
302 /** Call to end a drag `successfully'. Ungrabs item and calls
303 * subclass' finished() method.
305 * @param event GDK event, or 0.
306 * @return true if some movement occurred, otherwise false.
309 Drag::end_grab (GdkEvent* event)
311 _editor->stop_canvas_autoscroll ();
315 finished (event, _move_threshold_passed);
317 _editor->verbose_cursor()->hide ();
320 return _move_threshold_passed;
324 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
328 if (f > _pointer_frame_offset) {
329 pos = f - _pointer_frame_offset;
333 _editor->snap_to_with_modifier (pos, event);
340 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
342 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
346 Drag::current_pointer_x() const
348 return _drags->current_pointer_x ();
352 Drag::current_pointer_y () const
354 if (!_trackview_only) {
355 return _drags->current_pointer_y ();
358 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
362 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
364 /* check to see if we have moved in any way that matters since the last motion event */
365 if (_move_threshold_passed &&
366 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
367 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
371 pair<framecnt_t, int> const threshold = move_threshold ();
373 bool const old_move_threshold_passed = _move_threshold_passed;
375 if (!_move_threshold_passed) {
377 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
378 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
380 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
383 if (active (_editor->mouse_mode) && _move_threshold_passed) {
385 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
386 if (!from_autoscroll) {
387 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
390 if (!_editor->autoscroll_active() || from_autoscroll) {
392 bool first_move = (_move_threshold_passed != old_move_threshold_passed) ||
394 motion (event, first_move && !_starting_point_passed);
396 if (first_move && !_starting_point_passed) {
397 _starting_point_passed = true;
400 _last_pointer_x = _drags->current_pointer_x ();
401 _last_pointer_y = current_pointer_y ();
402 _last_pointer_frame = adjusted_current_frame (event);
412 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
420 aborted (_move_threshold_passed);
422 _editor->stop_canvas_autoscroll ();
423 _editor->verbose_cursor()->hide ();
427 Drag::show_verbose_cursor_time (framepos_t frame)
429 _editor->verbose_cursor()->set_time (frame);
430 _editor->verbose_cursor()->show ();
434 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
436 _editor->verbose_cursor()->set_duration (start, end);
437 _editor->verbose_cursor()->show ();
441 Drag::show_verbose_cursor_text (string const & text)
443 _editor->verbose_cursor()->set (text);
444 _editor->verbose_cursor()->show ();
447 boost::shared_ptr<Region>
448 Drag::add_midi_region (MidiTimeAxisView* view)
450 if (_editor->session()) {
451 const TempoMap& map (_editor->session()->tempo_map());
452 framecnt_t pos = grab_frame();
453 const Meter& m = map.meter_at (pos);
454 /* not that the frame rate used here can be affected by pull up/down which
457 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
458 return view->add_region (grab_frame(), len, true);
461 return boost::shared_ptr<Region>();
464 struct EditorOrderTimeAxisViewSorter {
465 bool operator() (TimeAxisView* a, TimeAxisView* b) {
466 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
467 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
469 return ra->route()->order_key () < rb->route()->order_key ();
473 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
477 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
479 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
480 as some of the regions we are dragging may be on such tracks.
483 TrackViewList track_views = _editor->track_views;
484 track_views.sort (EditorOrderTimeAxisViewSorter ());
486 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
487 _time_axis_views.push_back (*i);
489 TimeAxisView::Children children_list = (*i)->get_child_list ();
490 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
491 _time_axis_views.push_back (j->get());
495 /* the list of views can be empty at this point if this is a region list-insert drag
498 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
499 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
502 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
506 RegionDrag::region_going_away (RegionView* v)
508 list<DraggingView>::iterator i = _views.begin ();
509 while (i != _views.end() && i->view != v) {
513 if (i != _views.end()) {
518 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
519 * or -1 if it is not found.
522 RegionDrag::find_time_axis_view (TimeAxisView* t) const
525 int const N = _time_axis_views.size ();
526 while (i < N && _time_axis_views[i] != t) {
537 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
538 : RegionDrag (e, i, p, v)
541 , _last_pointer_time_axis_view (0)
542 , _last_pointer_layer (0)
544 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
548 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
550 Drag::start_grab (event, cursor);
552 show_verbose_cursor_time (_last_frame_position);
554 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
556 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
557 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
562 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
564 /* compute the amount of pointer motion in frames, and where
565 the region would be if we moved it by that much.
567 *pending_region_position = adjusted_current_frame (event);
569 framepos_t sync_frame;
570 framecnt_t sync_offset;
573 sync_offset = _primary->region()->sync_offset (sync_dir);
575 /* we don't handle a sync point that lies before zero.
577 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
579 sync_frame = *pending_region_position + (sync_dir*sync_offset);
581 _editor->snap_to_with_modifier (sync_frame, event);
583 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
586 *pending_region_position = _last_frame_position;
589 if (*pending_region_position > max_framepos - _primary->region()->length()) {
590 *pending_region_position = _last_frame_position;
595 /* in locked edit mode, reverse the usual meaning of _x_constrained */
596 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
598 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
600 /* x movement since last time (in pixels) */
601 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
603 /* total x movement */
604 framecnt_t total_dx = *pending_region_position;
605 if (regions_came_from_canvas()) {
606 total_dx = total_dx - grab_frame ();
609 /* check that no regions have gone off the start of the session */
610 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
611 if ((i->view->region()->position() + total_dx) < 0) {
613 *pending_region_position = _last_frame_position;
624 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
626 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
627 int const n = i->time_axis_view + delta_track;
628 if (n < 0 || n >= int (_time_axis_views.size())) {
629 /* off the top or bottom track */
633 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
634 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
635 /* not a track, or the wrong type */
639 double const l = i->layer + delta_layer;
641 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
642 mode to allow the user to place a region below another on layer 0.
644 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
645 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
646 If it has, the layers will be munged later anyway, so it's ok.
652 /* all regions being dragged are ok with this change */
657 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
659 double delta_layer = 0;
660 int delta_time_axis_view = 0;
662 assert (!_views.empty ());
664 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
666 /* Find the TimeAxisView that the pointer is now over */
667 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
668 TimeAxisView* tv = r.first;
670 if (tv && tv->view()) {
671 double layer = r.second;
673 if (first_move && tv->view()->layer_display() == Stacked) {
674 tv->view()->set_layer_display (Expanded);
677 /* Here's the current pointer position in terms of time axis view and layer */
678 int const current_pointer_time_axis_view = find_time_axis_view (tv);
679 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
681 /* Work out the change in y */
683 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
684 delta_layer = current_pointer_layer - _last_pointer_layer;
687 /* Work out the change in x */
688 framepos_t pending_region_position;
689 double const x_delta = compute_x_delta (event, &pending_region_position);
690 _last_frame_position = pending_region_position;
692 /* Verify change in y */
693 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
694 /* this y movement is not allowed, so do no y movement this time */
695 delta_time_axis_view = 0;
699 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
700 /* haven't reached next snap point, and we're not switching
701 trackviews nor layers. nothing to do.
706 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
708 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
710 RegionView* rv = i->view;
715 if (rv->region()->locked() || rv->region()->video_locked()) {
722 /* reparent the regionview into a group above all
726 ArdourCanvas::Item* rvg = rv->get_canvas_group();
727 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
728 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
729 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
730 /* move the item so that it continues to appear at the
731 same location now that its parent has changed.
733 rvg->move (rv_canvas_offset - dmg_canvas_offset);
736 /* If we have moved tracks, we'll fudge the layer delta so that the
737 region gets moved back onto layer 0 on its new track; this avoids
738 confusion when dragging regions from non-zero layers onto different
741 double this_delta_layer = delta_layer;
742 if (delta_time_axis_view != 0) {
743 this_delta_layer = - i->layer;
750 if (i->time_axis_view >= 0) {
751 track_index = i->time_axis_view + delta_time_axis_view;
753 track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
756 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
760 /* The TimeAxisView that this region is now over */
761 TimeAxisView* current_tv = _time_axis_views[track_index];
763 /* Ensure it is moved from stacked -> expanded if appropriate */
764 if (current_tv->view()->layer_display() == Stacked) {
765 current_tv->view()->set_layer_display (Expanded);
768 /* We're only allowed to go -ve in layer on Expanded views */
769 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
770 this_delta_layer = - i->layer;
774 rv->set_height (current_tv->view()->child_height ());
776 /* Update show/hidden status as the region view may have come from a hidden track,
777 or have moved to one.
779 if (current_tv->hidden ()) {
780 rv->get_canvas_group()->hide ();
782 rv->get_canvas_group()->show ();
785 /* Update the DraggingView */
786 i->time_axis_view = track_index;
787 i->layer += this_delta_layer;
790 _editor->mouse_brush_insert_region (rv, pending_region_position);
794 /* Get the y coordinate of the top of the track that this region is now over */
795 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
797 /* And adjust for the layer that it should be on */
798 StreamView* cv = current_tv->view ();
799 switch (cv->layer_display ()) {
803 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
806 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
810 /* need to get the parent of the regionview
811 * canvas group and get its position in
812 * equivalent coordinate space as the trackview
813 * we are now dragging over.
816 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
823 /* Only move the region into the empty dropzone at the bottom if the pointer
827 if (current_pointer_y() >= 0) {
829 Coord last_track_bottom_edge;
830 if (!_time_axis_views.empty()) {
831 TimeAxisView* last = _time_axis_views.back();
832 last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
834 last_track_bottom_edge = 0;
837 y_delta = last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y;
838 i->time_axis_view = -1;
842 /* Now move the region view */
843 rv->move (x_delta, y_delta);
845 } /* foreach region */
847 _total_x_delta += x_delta;
849 if (x_delta != 0 && !_brushing) {
850 show_verbose_cursor_time (_last_frame_position);
853 _last_pointer_time_axis_view += delta_time_axis_view;
854 _last_pointer_layer += delta_layer;
858 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
860 if (_copy && first_move) {
862 if (_x_constrained) {
863 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
865 _editor->begin_reversible_command (Operations::region_copy);
868 /* duplicate the regionview(s) and region(s) */
870 list<DraggingView> new_regionviews;
872 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
874 RegionView* rv = i->view;
875 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
876 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
878 const boost::shared_ptr<const Region> original = rv->region();
879 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
880 region_copy->set_position (original->position());
884 boost::shared_ptr<AudioRegion> audioregion_copy
885 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
887 nrv = new AudioRegionView (*arv, audioregion_copy);
889 boost::shared_ptr<MidiRegion> midiregion_copy
890 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
891 nrv = new MidiRegionView (*mrv, midiregion_copy);
896 nrv->get_canvas_group()->show ();
897 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
899 /* swap _primary to the copy */
901 if (rv == _primary) {
905 /* ..and deselect the one we copied */
907 rv->set_selected (false);
910 if (!new_regionviews.empty()) {
912 /* reflect the fact that we are dragging the copies */
914 _views = new_regionviews;
916 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
919 } else if (!_copy && first_move) {
921 if (_x_constrained) {
922 _editor->begin_reversible_command (_("fixed time region drag"));
924 _editor->begin_reversible_command (Operations::region_drag);
928 RegionMotionDrag::motion (event, first_move);
932 RegionMotionDrag::finished (GdkEvent *, bool)
934 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
939 if ((*i)->view()->layer_display() == Expanded) {
940 (*i)->view()->set_layer_display (Stacked);
946 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
948 RegionMotionDrag::finished (ev, movement_occurred);
950 if (!movement_occurred) {
954 if (was_double_click() && !_views.empty()) {
955 DraggingView dv = _views.front();
956 dv.view->show_region_editor ();
963 /* reverse this here so that we have the correct logic to finalize
967 if (Config->get_edit_mode() == Lock) {
968 _x_constrained = !_x_constrained;
971 assert (!_views.empty ());
973 /* We might have hidden region views so that they weren't visible during the drag
974 (when they have been reparented). Now everything can be shown again, as region
975 views are back in their track parent groups.
977 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
978 i->view->get_canvas_group()->show ();
981 bool const changed_position = (_last_frame_position != _primary->region()->position());
982 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
983 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1003 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1007 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1009 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1014 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1015 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1016 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1017 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1019 rtav->set_height (original->current_height());
1023 ChanCount one_midi_port (DataType::MIDI, 1);
1024 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1025 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1026 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1028 rtav->set_height (original->current_height());
1033 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1039 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1041 RegionSelection new_views;
1042 PlaylistSet modified_playlists;
1043 RouteTimeAxisView* new_time_axis_view = 0;
1046 /* all changes were made during motion event handlers */
1048 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1052 _editor->commit_reversible_command ();
1056 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1057 PlaylistMapping playlist_mapping;
1059 /* insert the regions into their new playlists */
1060 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1062 RouteTimeAxisView* dest_rtv = 0;
1064 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1070 if (changed_position && !_x_constrained) {
1071 where = i->view->region()->position() - drag_delta;
1073 where = i->view->region()->position();
1076 if (i->time_axis_view < 0) {
1077 /* dragged to drop zone */
1079 PlaylistMapping::iterator pm;
1081 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1082 /* first region from this original playlist: create a new track */
1083 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1084 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1085 dest_rtv = new_time_axis_view;
1087 /* we already created a new track for regions from this playlist, use it */
1088 dest_rtv = pm->second;
1091 /* destination time axis view is the one we dragged to */
1092 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1095 if (dest_rtv != 0) {
1096 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1097 if (new_view != 0) {
1098 new_views.push_back (new_view);
1102 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1103 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1106 list<DraggingView>::const_iterator next = i;
1112 /* If we've created new regions either by copying or moving
1113 to a new track, we want to replace the old selection with the new ones
1116 if (new_views.size() > 0) {
1117 _editor->selection->set (new_views);
1120 /* write commands for the accumulated diffs for all our modified playlists */
1121 add_stateful_diff_commands_for_playlists (modified_playlists);
1123 _editor->commit_reversible_command ();
1127 RegionMoveDrag::finished_no_copy (
1128 bool const changed_position,
1129 bool const changed_tracks,
1130 framecnt_t const drag_delta
1133 RegionSelection new_views;
1134 PlaylistSet modified_playlists;
1135 PlaylistSet frozen_playlists;
1136 set<RouteTimeAxisView*> views_to_update;
1137 RouteTimeAxisView* new_time_axis_view = 0;
1140 /* all changes were made during motion event handlers */
1141 _editor->commit_reversible_command ();
1145 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1146 PlaylistMapping playlist_mapping;
1148 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1150 RegionView* rv = i->view;
1151 RouteTimeAxisView* dest_rtv = 0;
1153 if (rv->region()->locked() || rv->region()->video_locked()) {
1158 if (i->time_axis_view < 0) {
1159 /* dragged to drop zone */
1161 PlaylistMapping::iterator pm;
1163 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1164 /* first region from this original playlist: create a new track */
1165 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1166 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1167 dest_rtv = new_time_axis_view;
1169 /* we already created a new track for regions from this playlist, use it */
1170 dest_rtv = pm->second;
1174 /* destination time axis view is the one we dragged to */
1175 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1180 double const dest_layer = i->layer;
1182 views_to_update.insert (dest_rtv);
1186 if (changed_position && !_x_constrained) {
1187 where = rv->region()->position() - drag_delta;
1189 where = rv->region()->position();
1192 if (changed_tracks) {
1194 /* insert into new playlist */
1196 RegionView* new_view = insert_region_into_playlist (
1197 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1200 if (new_view == 0) {
1205 new_views.push_back (new_view);
1207 /* remove from old playlist */
1209 /* the region that used to be in the old playlist is not
1210 moved to the new one - we use a copy of it. as a result,
1211 any existing editor for the region should no longer be
1214 rv->hide_region_editor();
1217 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1221 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1223 /* this movement may result in a crossfade being modified, or a layering change,
1224 so we need to get undo data from the playlist as well as the region.
1227 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1229 playlist->clear_changes ();
1232 rv->region()->clear_changes ();
1235 motion on the same track. plonk the previously reparented region
1236 back to its original canvas group (its streamview).
1237 No need to do anything for copies as they are fake regions which will be deleted.
1240 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1241 rv->get_canvas_group()->set_y_position (i->initial_y);
1244 /* just change the model */
1245 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1246 playlist->set_layer (rv->region(), dest_layer);
1249 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1251 r = frozen_playlists.insert (playlist);
1254 playlist->freeze ();
1257 rv->region()->set_position (where);
1259 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1262 if (changed_tracks) {
1264 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1265 was selected in all of them, then removing it from a playlist will have removed all
1266 trace of it from _views (i.e. there were N regions selected, we removed 1,
1267 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1268 corresponding regionview, and _views is now empty).
1270 This could have invalidated any and all iterators into _views.
1272 The heuristic we use here is: if the region selection is empty, break out of the loop
1273 here. if the region selection is not empty, then restart the loop because we know that
1274 we must have removed at least the region(view) we've just been working on as well as any
1275 that we processed on previous iterations.
1277 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1278 we can just iterate.
1282 if (_views.empty()) {
1293 /* If we've created new regions either by copying or moving
1294 to a new track, we want to replace the old selection with the new ones
1297 if (new_views.size() > 0) {
1298 _editor->selection->set (new_views);
1301 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1305 /* write commands for the accumulated diffs for all our modified playlists */
1306 add_stateful_diff_commands_for_playlists (modified_playlists);
1308 _editor->commit_reversible_command ();
1310 /* We have futzed with the layering of canvas items on our streamviews.
1311 If any region changed layer, this will have resulted in the stream
1312 views being asked to set up their region views, and all will be well.
1313 If not, we might now have badly-ordered region views. Ask the StreamViews
1314 involved to sort themselves out, just in case.
1317 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1318 (*i)->view()->playlist_layered ((*i)->track ());
1322 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1323 * @param region Region to remove.
1324 * @param playlist playlist To remove from.
1325 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1326 * that clear_changes () is only called once per playlist.
1329 RegionMoveDrag::remove_region_from_playlist (
1330 boost::shared_ptr<Region> region,
1331 boost::shared_ptr<Playlist> playlist,
1332 PlaylistSet& modified_playlists
1335 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1338 playlist->clear_changes ();
1341 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1345 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1346 * clearing the playlist's diff history first if necessary.
1347 * @param region Region to insert.
1348 * @param dest_rtv Destination RouteTimeAxisView.
1349 * @param dest_layer Destination layer.
1350 * @param where Destination position.
1351 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1352 * that clear_changes () is only called once per playlist.
1353 * @return New RegionView, or 0 if no insert was performed.
1356 RegionMoveDrag::insert_region_into_playlist (
1357 boost::shared_ptr<Region> region,
1358 RouteTimeAxisView* dest_rtv,
1361 PlaylistSet& modified_playlists
1364 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1365 if (!dest_playlist) {
1369 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1370 _new_region_view = 0;
1371 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1373 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1374 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1376 dest_playlist->clear_changes ();
1379 dest_playlist->add_region (region, where);
1381 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1382 dest_playlist->set_layer (region, dest_layer);
1387 assert (_new_region_view);
1389 return _new_region_view;
1393 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1395 _new_region_view = rv;
1399 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1401 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1402 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1404 _editor->session()->add_command (c);
1413 RegionMoveDrag::aborted (bool movement_occurred)
1417 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1418 list<DraggingView>::const_iterator next = i;
1427 RegionMotionDrag::aborted (movement_occurred);
1432 RegionMotionDrag::aborted (bool)
1434 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1436 StreamView* sview = (*i)->view();
1439 if (sview->layer_display() == Expanded) {
1440 sview->set_layer_display (Stacked);
1445 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1446 RegionView* rv = i->view;
1447 TimeAxisView* tv = &(rv->get_time_axis_view ());
1448 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1450 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1451 rv->get_canvas_group()->set_y_position (0);
1453 rv->move (-_total_x_delta, 0);
1454 rv->set_height (rtv->view()->child_height ());
1458 /** @param b true to brush, otherwise false.
1459 * @param c true to make copies of the regions being moved, otherwise false.
1461 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1462 : RegionMotionDrag (e, i, p, v, b),
1465 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1468 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1469 if (rtv && rtv->is_track()) {
1470 speed = rtv->track()->speed ();
1473 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1477 RegionMoveDrag::setup_pointer_frame_offset ()
1479 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1482 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1483 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1485 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1487 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1488 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1490 _primary = v->view()->create_region_view (r, false, false);
1492 _primary->get_canvas_group()->show ();
1493 _primary->set_position (pos, 0);
1494 _views.push_back (DraggingView (_primary, this, v));
1496 _last_frame_position = pos;
1498 _item = _primary->get_canvas_group ();
1502 RegionInsertDrag::finished (GdkEvent *, bool)
1504 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1506 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1507 _primary->get_canvas_group()->set_y_position (0);
1509 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1511 _editor->begin_reversible_command (Operations::insert_region);
1512 playlist->clear_changes ();
1513 playlist->add_region (_primary->region (), _last_frame_position);
1515 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1516 if (Config->get_edit_mode() == Ripple) {
1517 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1520 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1521 _editor->commit_reversible_command ();
1529 RegionInsertDrag::aborted (bool)
1536 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1537 : RegionMoveDrag (e, i, p, v, false, false)
1539 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1542 struct RegionSelectionByPosition {
1543 bool operator() (RegionView*a, RegionView* b) {
1544 return a->region()->position () < b->region()->position();
1549 RegionSpliceDrag::motion (GdkEvent* event, bool)
1551 /* Which trackview is this ? */
1553 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1554 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1556 /* The region motion is only processed if the pointer is over
1560 if (!tv || !tv->is_track()) {
1561 /* To make sure we hide the verbose canvas cursor when the mouse is
1562 not held over an audio track.
1564 _editor->verbose_cursor()->hide ();
1567 _editor->verbose_cursor()->show ();
1572 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1578 RegionSelection copy;
1579 _editor->selection->regions.by_position(copy);
1581 framepos_t const pf = adjusted_current_frame (event);
1583 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1585 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1591 boost::shared_ptr<Playlist> playlist;
1593 if ((playlist = atv->playlist()) == 0) {
1597 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1602 if (pf < (*i)->region()->last_frame() + 1) {
1606 if (pf > (*i)->region()->first_frame()) {
1612 playlist->shuffle ((*i)->region(), dir);
1617 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1619 RegionMoveDrag::finished (event, movement_occurred);
1623 RegionSpliceDrag::aborted (bool)
1633 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1636 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1638 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1639 RegionSelection to_ripple;
1640 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1641 if ((*i)->position() >= where) {
1642 to_ripple.push_back (rtv->view()->find_view(*i));
1646 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1647 if (!exclude.contains (*i)) {
1648 // the selection has already been added to _views
1650 if (drag_in_progress) {
1651 // do the same things that RegionMotionDrag::motion does when
1652 // first_move is true, for the region views that we're adding
1653 // to _views this time
1656 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1657 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1658 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1659 rvg->reparent (_editor->_drag_motion_group);
1661 // we only need to move in the y direction
1662 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1667 _views.push_back (DraggingView (*i, this, tav));
1673 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1676 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1677 // we added all the regions after the selection
1679 std::list<DraggingView>::iterator to_erase = i++;
1680 if (!_editor->selection->regions.contains (to_erase->view)) {
1681 // restore the non-selected regions to their original playlist & positions,
1682 // and then ripple them back by the length of the regions that were dragged away
1683 // do the same things as RegionMotionDrag::aborted
1685 RegionView *rv = to_erase->view;
1686 TimeAxisView* tv = &(rv->get_time_axis_view ());
1687 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1690 // plonk them back onto their own track
1691 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1692 rv->get_canvas_group()->set_y_position (0);
1696 // move the underlying region to match the view
1697 rv->region()->set_position (rv->region()->position() + amount);
1699 // restore the view to match the underlying region's original position
1700 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
1703 rv->set_height (rtv->view()->child_height ());
1704 _views.erase (to_erase);
1710 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const
1712 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) {
1714 return allow_moves_across_tracks;
1722 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1723 : RegionMoveDrag (e, i, p, v, false, false)
1725 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
1726 // compute length of selection
1727 RegionSelection selected_regions = _editor->selection->regions;
1728 selection_length = selected_regions.end_frame() - selected_regions.start();
1730 // we'll only allow dragging to another track in ripple mode if all the regions
1731 // being dragged start off on the same track
1732 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
1735 exclude = new RegionList;
1736 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
1737 exclude->push_back((*i)->region());
1740 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
1741 RegionSelection copy;
1742 selected_regions.by_position(copy); // get selected regions sorted by position into copy
1744 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
1745 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1747 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1748 // find ripple start point on each applicable playlist
1749 RegionView *first_selected_on_this_track = NULL;
1750 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1751 if ((*i)->region()->playlist() == (*pi)) {
1752 // region is on this playlist - it's the first, because they're sorted
1753 first_selected_on_this_track = *i;
1757 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
1758 add_all_after_to_views (
1759 &first_selected_on_this_track->get_time_axis_view(),
1760 first_selected_on_this_track->region()->position(),
1761 selected_regions, false);
1764 if (allow_moves_across_tracks) {
1765 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
1773 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
1775 /* Which trackview is this ? */
1777 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1778 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1780 /* The region motion is only processed if the pointer is over
1784 if (!tv || !tv->is_track()) {
1785 /* To make sure we hide the verbose canvas cursor when the mouse is
1786 not held over an audiotrack.
1788 _editor->verbose_cursor()->hide ();
1792 framepos_t where = adjusted_current_frame (event);
1793 assert (where >= 0);
1795 double delta = compute_x_delta (event, &after);
1797 framecnt_t amount = _editor->pixel_to_sample (delta);
1799 if (allow_moves_across_tracks) {
1800 // all the originally selected regions were on the same track
1802 framecnt_t adjust = 0;
1803 if (prev_tav && tv != prev_tav) {
1804 // dragged onto a different track
1805 // remove the unselected regions from _views, restore them to their original positions
1806 // and add the regions after the drop point on the new playlist to _views instead.
1807 // undo the effect of rippling the previous playlist, and include the effect of removing
1808 // the dragged region(s) from this track
1810 remove_unselected_from_views (prev_amount, false);
1811 // ripple previous playlist according to the regions that have been removed onto the new playlist
1812 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
1815 // move just the selected regions
1816 RegionMoveDrag::motion(event, first_move);
1818 // ensure that the ripple operation on the new playlist inserts selection_length time
1819 adjust = selection_length;
1820 // ripple the new current playlist
1821 tv->playlist()->ripple (where, amount+adjust, exclude);
1823 // add regions after point where drag entered this track to subsequent ripples
1824 add_all_after_to_views (tv, where, _editor->selection->regions, true);
1827 // motion on same track
1828 RegionMoveDrag::motion(event, first_move);
1832 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
1833 prev_position = where;
1835 // selection encompasses multiple tracks - just drag
1836 // cross-track drags are forbidden
1837 RegionMoveDrag::motion(event, first_move);
1840 if (!_x_constrained) {
1841 prev_amount += amount;
1844 _last_frame_position = after;
1848 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
1850 if (!movement_occurred) {
1854 if (was_double_click() && !_views.empty()) {
1855 DraggingView dv = _views.front();
1856 dv.view->show_region_editor ();
1863 _editor->begin_reversible_command(_("Ripple drag"));
1865 // remove the regions being rippled from the dragging view, updating them to
1866 // their new positions
1867 remove_unselected_from_views (prev_amount, true);
1869 if (allow_moves_across_tracks) {
1871 // if regions were dragged across tracks, we've rippled any later
1872 // regions on the track the regions were dragged off, so we need
1873 // to add the original track to the undo record
1874 orig_tav->playlist()->clear_changes();
1875 vector<Command*> cmds;
1876 orig_tav->playlist()->rdiff (cmds);
1877 _editor->session()->add_commands (cmds);
1879 if (prev_tav && prev_tav != orig_tav) {
1880 prev_tav->playlist()->clear_changes();
1881 vector<Command*> cmds;
1882 prev_tav->playlist()->rdiff (cmds);
1883 _editor->session()->add_commands (cmds);
1886 // selection spanned multiple tracks - all will need adding to undo record
1888 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
1889 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1891 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1892 (*pi)->clear_changes();
1893 vector<Command*> cmds;
1894 (*pi)->rdiff (cmds);
1895 _editor->session()->add_commands (cmds);
1899 // other modified playlists are added to undo by RegionMoveDrag::finished()
1900 RegionMoveDrag::finished (event, movement_occurred);
1901 _editor->commit_reversible_command();
1905 RegionRippleDrag::aborted (bool movement_occurred)
1907 RegionMoveDrag::aborted (movement_occurred);
1912 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1914 _view (dynamic_cast<MidiTimeAxisView*> (v))
1916 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1922 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1925 _region = add_midi_region (_view);
1926 _view->playlist()->freeze ();
1929 framepos_t const f = adjusted_current_frame (event);
1930 if (f < grab_frame()) {
1931 _region->set_position (f);
1934 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1935 so that if this region is duplicated, its duplicate starts on
1936 a snap point rather than 1 frame after a snap point. Otherwise things get
1937 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1938 place snapped notes at the start of the region.
1941 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1942 _region->set_length (len < 1 ? 1 : len);
1948 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1950 if (!movement_occurred) {
1951 add_midi_region (_view);
1953 _view->playlist()->thaw ();
1958 RegionCreateDrag::aborted (bool)
1961 _view->playlist()->thaw ();
1967 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1971 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1975 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1977 Gdk::Cursor* cursor;
1978 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1980 float x_fraction = cnote->mouse_x_fraction ();
1982 if (x_fraction > 0.0 && x_fraction < 0.25) {
1983 cursor = _editor->cursors()->left_side_trim;
1986 cursor = _editor->cursors()->right_side_trim;
1990 Drag::start_grab (event, cursor);
1992 region = &cnote->region_view();
1996 if (event->motion.state & Keyboard::PrimaryModifier) {
2002 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2004 if (ms.size() > 1) {
2005 /* has to be relative, may make no sense otherwise */
2009 /* select this note; if it is already selected, preserve the existing selection,
2010 otherwise make this note the only one selected.
2012 region->note_selected (cnote, cnote->selected ());
2014 _editor->begin_reversible_command (_("resize notes"));
2016 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2017 MidiRegionSelection::iterator next;
2020 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2022 mrv->begin_resizing (at_front);
2029 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
2031 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2032 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2033 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2035 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2037 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2043 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
2045 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2046 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2047 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2049 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2051 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2055 _editor->commit_reversible_command ();
2059 NoteResizeDrag::aborted (bool)
2061 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2062 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2063 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2065 mrv->abort_resizing ();
2070 AVDraggingView::AVDraggingView (RegionView* v)
2073 initial_position = v->region()->position ();
2076 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2079 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2082 TrackViewList empty;
2084 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2085 std::list<RegionView*> views = rs.by_layer();
2087 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2088 RegionView* rv = (*i);
2089 if (!rv->region()->video_locked()) {
2092 _views.push_back (AVDraggingView (rv));
2097 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2099 Drag::start_grab (event);
2100 if (_editor->session() == 0) {
2104 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2105 _max_backwards_drag = (
2106 ARDOUR_UI::instance()->video_timeline->get_duration()
2107 + ARDOUR_UI::instance()->video_timeline->get_offset()
2108 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2111 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2112 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2113 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2116 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2119 Timecode::Time timecode;
2120 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2121 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);
2122 show_verbose_cursor_text (buf);
2126 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2128 if (_editor->session() == 0) {
2131 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2135 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2136 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2138 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2139 dt = - _max_backwards_drag;
2142 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2143 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2145 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2146 RegionView* rv = i->view;
2147 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2150 rv->region()->clear_changes ();
2151 rv->region()->suspend_property_changes();
2153 rv->region()->set_position(i->initial_position + dt);
2154 rv->region_changed(ARDOUR::Properties::position);
2157 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2158 Timecode::Time timecode;
2159 Timecode::Time timediff;
2161 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2162 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2163 snprintf (buf, sizeof (buf),
2164 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2165 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2166 , _("Video Start:"),
2167 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2169 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2171 show_verbose_cursor_text (buf);
2175 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2177 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2181 if (!movement_occurred || ! _editor->session()) {
2185 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2187 _editor->begin_reversible_command (_("Move Video"));
2189 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2190 ARDOUR_UI::instance()->video_timeline->save_undo();
2191 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2192 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2194 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2195 i->view->drag_end();
2196 i->view->region()->resume_property_changes ();
2198 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2201 _editor->session()->maybe_update_session_range(
2202 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2203 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2207 _editor->commit_reversible_command ();
2211 VideoTimeLineDrag::aborted (bool)
2213 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2216 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2217 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2219 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2220 i->view->region()->resume_property_changes ();
2221 i->view->region()->set_position(i->initial_position);
2225 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2226 : RegionDrag (e, i, p, v)
2227 , _preserve_fade_anchor (preserve_fade_anchor)
2228 , _jump_position_when_done (false)
2230 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2234 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2237 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2238 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2240 if (tv && tv->is_track()) {
2241 speed = tv->track()->speed();
2244 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2245 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2246 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2248 framepos_t const pf = adjusted_current_frame (event);
2250 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2251 /* Move the contents of the region around without changing the region bounds */
2252 _operation = ContentsTrim;
2253 Drag::start_grab (event, _editor->cursors()->trimmer);
2255 /* These will get overridden for a point trim.*/
2256 if (pf < (region_start + region_length/2)) {
2257 /* closer to front */
2258 _operation = StartTrim;
2260 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2261 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2263 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2267 _operation = EndTrim;
2268 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2269 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2271 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2276 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2277 _jump_position_when_done = true;
2280 switch (_operation) {
2282 show_verbose_cursor_time (region_start);
2283 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2284 i->view->trim_front_starting ();
2288 show_verbose_cursor_time (region_end);
2291 show_verbose_cursor_time (pf);
2295 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2296 i->view->region()->suspend_property_changes ();
2301 TrimDrag::motion (GdkEvent* event, bool first_move)
2303 RegionView* rv = _primary;
2306 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2307 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2308 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2309 frameoffset_t frame_delta = 0;
2311 if (tv && tv->is_track()) {
2312 speed = tv->track()->speed();
2315 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2321 switch (_operation) {
2323 trim_type = "Region start trim";
2326 trim_type = "Region end trim";
2329 trim_type = "Region content trim";
2336 _editor->begin_reversible_command (trim_type);
2338 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2339 RegionView* rv = i->view;
2340 rv->enable_display (false);
2341 rv->region()->playlist()->clear_owned_changes ();
2343 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2346 arv->temporarily_hide_envelope ();
2350 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2351 insert_result = _editor->motion_frozen_playlists.insert (pl);
2353 if (insert_result.second) {
2359 bool non_overlap_trim = false;
2361 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2362 non_overlap_trim = true;
2365 /* contstrain trim to fade length */
2366 if (_preserve_fade_anchor) {
2367 switch (_operation) {
2369 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2370 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2372 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2373 if (ar->locked()) continue;
2374 framecnt_t len = ar->fade_in()->back()->when;
2375 if (len < dt) dt = min(dt, len);
2379 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2380 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2382 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2383 if (ar->locked()) continue;
2384 framecnt_t len = ar->fade_out()->back()->when;
2385 if (len < -dt) dt = max(dt, -len);
2394 switch (_operation) {
2396 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2397 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2398 if (changed && _preserve_fade_anchor) {
2399 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2401 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2402 framecnt_t len = ar->fade_in()->back()->when;
2403 framecnt_t diff = ar->first_frame() - i->initial_position;
2404 framepos_t new_length = len - diff;
2405 i->anchored_fade_length = min (ar->length(), new_length);
2406 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2407 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2414 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2415 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2416 if (changed && _preserve_fade_anchor) {
2417 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2419 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2420 framecnt_t len = ar->fade_out()->back()->when;
2421 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2422 framepos_t new_length = len + diff;
2423 i->anchored_fade_length = min (ar->length(), new_length);
2424 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2425 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2433 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2435 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2436 i->view->move_contents (frame_delta);
2442 switch (_operation) {
2444 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2447 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2450 // show_verbose_cursor_time (frame_delta);
2457 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2459 if (movement_occurred) {
2460 motion (event, false);
2462 if (_operation == StartTrim) {
2463 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2465 /* This must happen before the region's StatefulDiffCommand is created, as it may
2466 `correct' (ahem) the region's _start from being negative to being zero. It
2467 needs to be zero in the undo record.
2469 i->view->trim_front_ending ();
2471 if (_preserve_fade_anchor) {
2472 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2474 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2475 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2476 ar->set_fade_in_length(i->anchored_fade_length);
2477 ar->set_fade_in_active(true);
2480 if (_jump_position_when_done) {
2481 i->view->region()->set_position (i->initial_position);
2484 } else if (_operation == EndTrim) {
2485 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2486 if (_preserve_fade_anchor) {
2487 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2489 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2490 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2491 ar->set_fade_out_length(i->anchored_fade_length);
2492 ar->set_fade_out_active(true);
2495 if (_jump_position_when_done) {
2496 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2501 if (!_views.empty()) {
2502 if (_operation == StartTrim) {
2503 _editor->maybe_locate_with_edit_preroll(
2504 _views.begin()->view->region()->position());
2506 if (_operation == EndTrim) {
2507 _editor->maybe_locate_with_edit_preroll(
2508 _views.begin()->view->region()->position() +
2509 _views.begin()->view->region()->length());
2513 if (!_editor->selection->selected (_primary)) {
2514 _primary->thaw_after_trim ();
2517 set<boost::shared_ptr<Playlist> > diffed_playlists;
2519 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2520 i->view->thaw_after_trim ();
2521 i->view->enable_display (true);
2523 /* Trimming one region may affect others on the playlist, so we need
2524 to get undo Commands from the whole playlist rather than just the
2525 region. Use diffed_playlists to make sure we don't diff a given
2526 playlist more than once.
2528 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2529 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2530 vector<Command*> cmds;
2532 _editor->session()->add_commands (cmds);
2533 diffed_playlists.insert (p);
2538 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2542 _editor->motion_frozen_playlists.clear ();
2543 _editor->commit_reversible_command();
2546 /* no mouse movement */
2547 _editor->point_trim (event, adjusted_current_frame (event));
2550 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2551 if (_operation == StartTrim) {
2552 i->view->trim_front_ending ();
2555 i->view->region()->resume_property_changes ();
2560 TrimDrag::aborted (bool movement_occurred)
2562 /* Our motion method is changing model state, so use the Undo system
2563 to cancel. Perhaps not ideal, as this will leave an Undo point
2564 behind which may be slightly odd from the user's point of view.
2569 if (movement_occurred) {
2573 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2574 i->view->region()->resume_property_changes ();
2579 TrimDrag::setup_pointer_frame_offset ()
2581 list<DraggingView>::iterator i = _views.begin ();
2582 while (i != _views.end() && i->view != _primary) {
2586 if (i == _views.end()) {
2590 switch (_operation) {
2592 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2595 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2602 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2606 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2607 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2612 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2614 Drag::start_grab (event, cursor);
2615 show_verbose_cursor_time (adjusted_current_frame(event));
2619 MeterMarkerDrag::setup_pointer_frame_offset ()
2621 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2625 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2627 if (!_marker->meter().movable()) {
2633 // create a dummy marker for visual representation of moving the
2634 // section, because whether its a copy or not, we're going to
2635 // leave or lose the original marker (leave if its a copy; lose if its
2636 // not, because we'll remove it from the map).
2638 MeterSection section (_marker->meter());
2640 if (!section.movable()) {
2645 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2647 _marker = new MeterMarker (
2649 *_editor->meter_group,
2650 ARDOUR_UI::config()->color ("meter marker"),
2652 *new MeterSection (_marker->meter())
2655 /* use the new marker for the grab */
2656 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2659 TempoMap& map (_editor->session()->tempo_map());
2660 /* get current state */
2661 before_state = &map.get_state();
2662 /* remove the section while we drag it */
2663 map.remove_meter (section, true);
2667 framepos_t const pf = adjusted_current_frame (event);
2669 _marker->set_position (pf);
2670 show_verbose_cursor_time (pf);
2674 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2676 if (!movement_occurred) {
2677 if (was_double_click()) {
2678 _editor->edit_meter_marker (*_marker);
2683 if (!_marker->meter().movable()) {
2687 motion (event, false);
2689 Timecode::BBT_Time when;
2691 TempoMap& map (_editor->session()->tempo_map());
2692 map.bbt_time (last_pointer_frame(), when);
2694 if (_copy == true) {
2695 _editor->begin_reversible_command (_("copy meter mark"));
2696 XMLNode &before = map.get_state();
2697 map.add_meter (_marker->meter(), when);
2698 XMLNode &after = map.get_state();
2699 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2700 _editor->commit_reversible_command ();
2703 _editor->begin_reversible_command (_("move meter mark"));
2705 /* we removed it before, so add it back now */
2707 map.add_meter (_marker->meter(), when);
2708 XMLNode &after = map.get_state();
2709 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2710 _editor->commit_reversible_command ();
2713 // delete the dummy marker we used for visual representation while moving.
2714 // a new visual marker will show up automatically.
2719 MeterMarkerDrag::aborted (bool moved)
2721 _marker->set_position (_marker->meter().frame ());
2724 TempoMap& map (_editor->session()->tempo_map());
2725 /* we removed it before, so add it back now */
2726 map.add_meter (_marker->meter(), _marker->meter().frame());
2727 // delete the dummy marker we used for visual representation while moving.
2728 // a new visual marker will show up automatically.
2733 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2737 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2739 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2744 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2746 Drag::start_grab (event, cursor);
2747 show_verbose_cursor_time (adjusted_current_frame (event));
2751 TempoMarkerDrag::setup_pointer_frame_offset ()
2753 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2757 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2759 if (!_marker->tempo().movable()) {
2765 // create a dummy marker for visual representation of moving the
2766 // section, because whether its a copy or not, we're going to
2767 // leave or lose the original marker (leave if its a copy; lose if its
2768 // not, because we'll remove it from the map).
2770 // create a dummy marker for visual representation of moving the copy.
2771 // The actual copying is not done before we reach the finish callback.
2774 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2776 TempoSection section (_marker->tempo());
2778 _marker = new TempoMarker (
2780 *_editor->tempo_group,
2781 ARDOUR_UI::config()->color ("tempo marker"),
2783 *new TempoSection (_marker->tempo())
2786 /* use the new marker for the grab */
2787 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2790 TempoMap& map (_editor->session()->tempo_map());
2791 /* get current state */
2792 before_state = &map.get_state();
2793 /* remove the section while we drag it */
2794 map.remove_tempo (section, true);
2798 framepos_t const pf = adjusted_current_frame (event);
2799 _marker->set_position (pf);
2800 show_verbose_cursor_time (pf);
2804 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2806 if (!movement_occurred) {
2807 if (was_double_click()) {
2808 _editor->edit_tempo_marker (*_marker);
2813 if (!_marker->tempo().movable()) {
2817 motion (event, false);
2819 TempoMap& map (_editor->session()->tempo_map());
2820 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
2821 Timecode::BBT_Time when;
2823 map.bbt_time (beat_time, when);
2825 if (_copy == true) {
2826 _editor->begin_reversible_command (_("copy tempo mark"));
2827 XMLNode &before = map.get_state();
2828 map.add_tempo (_marker->tempo(), when);
2829 XMLNode &after = map.get_state();
2830 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2831 _editor->commit_reversible_command ();
2834 _editor->begin_reversible_command (_("move tempo mark"));
2835 /* we removed it before, so add it back now */
2836 map.add_tempo (_marker->tempo(), when);
2837 XMLNode &after = map.get_state();
2838 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2839 _editor->commit_reversible_command ();
2842 // delete the dummy marker we used for visual representation while moving.
2843 // a new visual marker will show up automatically.
2848 TempoMarkerDrag::aborted (bool moved)
2850 _marker->set_position (_marker->tempo().frame());
2852 TempoMap& map (_editor->session()->tempo_map());
2853 /* we removed it before, so add it back now */
2854 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2855 // delete the dummy marker we used for visual representation while moving.
2856 // a new visual marker will show up automatically.
2861 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2862 : Drag (e, &c.track_canvas_item(), false)
2866 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2869 /** Do all the things we do when dragging the playhead to make it look as though
2870 * we have located, without actually doing the locate (because that would cause
2871 * the diskstream buffers to be refilled, which is too slow).
2874 CursorDrag::fake_locate (framepos_t t)
2876 _editor->playhead_cursor->set_position (t);
2878 Session* s = _editor->session ();
2879 if (s->timecode_transmission_suspended ()) {
2880 framepos_t const f = _editor->playhead_cursor->current_frame ();
2881 /* This is asynchronous so it will be sent "now"
2883 s->send_mmc_locate (f);
2884 /* These are synchronous and will be sent during the next
2887 s->queue_full_time_code ();
2888 s->queue_song_position_pointer ();
2891 show_verbose_cursor_time (t);
2892 _editor->UpdateAllTransportClocks (t);
2896 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2898 Drag::start_grab (event, c);
2900 _grab_zoom = _editor->samples_per_pixel;
2902 framepos_t where = _editor->canvas_event_sample (event);
2904 _editor->snap_to_with_modifier (where, event);
2906 _editor->_dragging_playhead = true;
2908 Session* s = _editor->session ();
2910 /* grab the track canvas item as well */
2912 _cursor.track_canvas_item().grab();
2915 if (_was_rolling && _stop) {
2919 if (s->is_auditioning()) {
2920 s->cancel_audition ();
2924 if (AudioEngine::instance()->connected()) {
2926 /* do this only if we're the engine is connected
2927 * because otherwise this request will never be
2928 * serviced and we'll busy wait forever. likewise,
2929 * notice if we are disconnected while waiting for the
2930 * request to be serviced.
2933 s->request_suspend_timecode_transmission ();
2934 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2935 /* twiddle our thumbs */
2940 fake_locate (where);
2944 CursorDrag::motion (GdkEvent* event, bool)
2946 framepos_t const adjusted_frame = adjusted_current_frame (event);
2947 if (adjusted_frame != last_pointer_frame()) {
2948 fake_locate (adjusted_frame);
2953 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2955 _editor->_dragging_playhead = false;
2957 _cursor.track_canvas_item().ungrab();
2959 if (!movement_occurred && _stop) {
2963 motion (event, false);
2965 Session* s = _editor->session ();
2967 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2968 _editor->_pending_locate_request = true;
2969 s->request_resume_timecode_transmission ();
2974 CursorDrag::aborted (bool)
2976 _cursor.track_canvas_item().ungrab();
2978 if (_editor->_dragging_playhead) {
2979 _editor->session()->request_resume_timecode_transmission ();
2980 _editor->_dragging_playhead = false;
2983 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2986 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2987 : RegionDrag (e, i, p, v)
2989 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2993 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2995 Drag::start_grab (event, cursor);
2997 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2998 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3000 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3004 FadeInDrag::setup_pointer_frame_offset ()
3006 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3007 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3008 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3012 FadeInDrag::motion (GdkEvent* event, bool)
3014 framecnt_t fade_length;
3016 framepos_t const pos = adjusted_current_frame (event);
3018 boost::shared_ptr<Region> region = _primary->region ();
3020 if (pos < (region->position() + 64)) {
3021 fade_length = 64; // this should be a minimum defined somewhere
3022 } else if (pos > region->last_frame()) {
3023 fade_length = region->length();
3025 fade_length = pos - region->position();
3028 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3030 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3036 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3039 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3043 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3045 if (!movement_occurred) {
3049 framecnt_t fade_length;
3051 framepos_t const pos = adjusted_current_frame (event);
3053 boost::shared_ptr<Region> region = _primary->region ();
3055 if (pos < (region->position() + 64)) {
3056 fade_length = 64; // this should be a minimum defined somewhere
3057 } else if (pos > region->last_frame()) {
3058 fade_length = region->length();
3060 fade_length = pos - region->position();
3063 _editor->begin_reversible_command (_("change fade in length"));
3065 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3067 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3073 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3074 XMLNode &before = alist->get_state();
3076 tmp->audio_region()->set_fade_in_length (fade_length);
3077 tmp->audio_region()->set_fade_in_active (true);
3079 XMLNode &after = alist->get_state();
3080 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3083 _editor->commit_reversible_command ();
3087 FadeInDrag::aborted (bool)
3089 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3090 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3096 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3100 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3101 : RegionDrag (e, i, p, v)
3103 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3107 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3109 Drag::start_grab (event, cursor);
3111 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3112 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3114 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3118 FadeOutDrag::setup_pointer_frame_offset ()
3120 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3121 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3122 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3126 FadeOutDrag::motion (GdkEvent* event, bool)
3128 framecnt_t fade_length;
3130 framepos_t const pos = adjusted_current_frame (event);
3132 boost::shared_ptr<Region> region = _primary->region ();
3134 if (pos > (region->last_frame() - 64)) {
3135 fade_length = 64; // this should really be a minimum fade defined somewhere
3137 else if (pos < region->position()) {
3138 fade_length = region->length();
3141 fade_length = region->last_frame() - pos;
3144 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3146 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3152 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3155 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3159 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3161 if (!movement_occurred) {
3165 framecnt_t fade_length;
3167 framepos_t const pos = adjusted_current_frame (event);
3169 boost::shared_ptr<Region> region = _primary->region ();
3171 if (pos > (region->last_frame() - 64)) {
3172 fade_length = 64; // this should really be a minimum fade defined somewhere
3174 else if (pos < region->position()) {
3175 fade_length = region->length();
3178 fade_length = region->last_frame() - pos;
3181 _editor->begin_reversible_command (_("change fade out length"));
3183 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3185 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3191 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3192 XMLNode &before = alist->get_state();
3194 tmp->audio_region()->set_fade_out_length (fade_length);
3195 tmp->audio_region()->set_fade_out_active (true);
3197 XMLNode &after = alist->get_state();
3198 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3201 _editor->commit_reversible_command ();
3205 FadeOutDrag::aborted (bool)
3207 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3208 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3214 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3218 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3221 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3223 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3226 _points.push_back (ArdourCanvas::Duple (0, 0));
3227 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3230 MarkerDrag::~MarkerDrag ()
3232 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3237 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3239 location = new Location (*l);
3240 markers.push_back (m);
3245 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3247 Drag::start_grab (event, cursor);
3251 Location *location = _editor->find_location_from_marker (_marker, is_start);
3252 _editor->_dragging_edit_point = true;
3254 update_item (location);
3256 // _drag_line->show();
3257 // _line->raise_to_top();
3260 show_verbose_cursor_time (location->start());
3262 show_verbose_cursor_time (location->end());
3265 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3268 case Selection::Toggle:
3269 /* we toggle on the button release */
3271 case Selection::Set:
3272 if (!_editor->selection->selected (_marker)) {
3273 _editor->selection->set (_marker);
3276 case Selection::Extend:
3278 Locations::LocationList ll;
3279 list<Marker*> to_add;
3281 _editor->selection->markers.range (s, e);
3282 s = min (_marker->position(), s);
3283 e = max (_marker->position(), e);
3286 if (e < max_framepos) {
3289 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3290 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3291 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3294 to_add.push_back (lm->start);
3297 to_add.push_back (lm->end);
3301 if (!to_add.empty()) {
3302 _editor->selection->add (to_add);
3306 case Selection::Add:
3307 _editor->selection->add (_marker);
3311 /* Set up copies for us to manipulate during the drag
3314 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3316 Location* l = _editor->find_location_from_marker (*i, is_start);
3323 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3325 /* range: check that the other end of the range isn't
3328 CopiedLocationInfo::iterator x;
3329 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3330 if (*(*x).location == *l) {
3334 if (x == _copied_locations.end()) {
3335 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3337 (*x).markers.push_back (*i);
3338 (*x).move_both = true;
3346 MarkerDrag::setup_pointer_frame_offset ()
3349 Location *location = _editor->find_location_from_marker (_marker, is_start);
3350 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3354 MarkerDrag::motion (GdkEvent* event, bool)
3356 framecnt_t f_delta = 0;
3358 bool move_both = false;
3359 Location *real_location;
3360 Location *copy_location = 0;
3362 framepos_t const newframe = adjusted_current_frame (event);
3363 framepos_t next = newframe;
3365 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3369 CopiedLocationInfo::iterator x;
3371 /* find the marker we're dragging, and compute the delta */
3373 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3375 copy_location = (*x).location;
3377 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3379 /* this marker is represented by this
3380 * CopiedLocationMarkerInfo
3383 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3388 if (real_location->is_mark()) {
3389 f_delta = newframe - copy_location->start();
3393 switch (_marker->type()) {
3394 case Marker::SessionStart:
3395 case Marker::RangeStart:
3396 case Marker::LoopStart:
3397 case Marker::PunchIn:
3398 f_delta = newframe - copy_location->start();
3401 case Marker::SessionEnd:
3402 case Marker::RangeEnd:
3403 case Marker::LoopEnd:
3404 case Marker::PunchOut:
3405 f_delta = newframe - copy_location->end();
3408 /* what kind of marker is this ? */
3417 if (x == _copied_locations.end()) {
3418 /* hmm, impossible - we didn't find the dragged marker */
3422 /* now move them all */
3424 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3426 copy_location = x->location;
3428 /* call this to find out if its the start or end */
3430 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3434 if (real_location->locked()) {
3438 if (copy_location->is_mark()) {
3442 copy_location->set_start (copy_location->start() + f_delta);
3446 framepos_t new_start = copy_location->start() + f_delta;
3447 framepos_t new_end = copy_location->end() + f_delta;
3449 if (is_start) { // start-of-range marker
3451 if (move_both || (*x).move_both) {
3452 copy_location->set_start (new_start);
3453 copy_location->set_end (new_end);
3454 } else if (new_start < copy_location->end()) {
3455 copy_location->set_start (new_start);
3456 } else if (newframe > 0) {
3457 _editor->snap_to (next, RoundUpAlways, true);
3458 copy_location->set_end (next);
3459 copy_location->set_start (newframe);
3462 } else { // end marker
3464 if (move_both || (*x).move_both) {
3465 copy_location->set_end (new_end);
3466 copy_location->set_start (new_start);
3467 } else if (new_end > copy_location->start()) {
3468 copy_location->set_end (new_end);
3469 } else if (newframe > 0) {
3470 _editor->snap_to (next, RoundDownAlways, true);
3471 copy_location->set_start (next);
3472 copy_location->set_end (newframe);
3477 update_item (copy_location);
3479 /* now lookup the actual GUI items used to display this
3480 * location and move them to wherever the copy of the location
3481 * is now. This means that the logic in ARDOUR::Location is
3482 * still enforced, even though we are not (yet) modifying
3483 * the real Location itself.
3486 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3489 lm->set_position (copy_location->start(), copy_location->end());
3494 assert (!_copied_locations.empty());
3496 show_verbose_cursor_time (newframe);
3500 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3502 if (!movement_occurred) {
3504 if (was_double_click()) {
3505 _editor->rename_marker (_marker);
3509 /* just a click, do nothing but finish
3510 off the selection process
3513 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3516 case Selection::Set:
3517 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3518 _editor->selection->set (_marker);
3522 case Selection::Toggle:
3523 /* we toggle on the button release, click only */
3524 _editor->selection->toggle (_marker);
3527 case Selection::Extend:
3528 case Selection::Add:
3535 _editor->_dragging_edit_point = false;
3537 _editor->begin_reversible_command ( _("move marker") );
3538 XMLNode &before = _editor->session()->locations()->get_state();
3540 MarkerSelection::iterator i;
3541 CopiedLocationInfo::iterator x;
3544 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3545 x != _copied_locations.end() && i != _editor->selection->markers.end();
3548 Location * location = _editor->find_location_from_marker (*i, is_start);
3552 if (location->locked()) {
3556 if (location->is_mark()) {
3557 location->set_start (((*x).location)->start());
3559 location->set (((*x).location)->start(), ((*x).location)->end());
3564 XMLNode &after = _editor->session()->locations()->get_state();
3565 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3566 _editor->commit_reversible_command ();
3570 MarkerDrag::aborted (bool movement_occured)
3572 if (!movement_occured) {
3576 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3578 /* move all markers to their original location */
3581 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3584 Location * location = _editor->find_location_from_marker (*m, is_start);
3587 (*m)->set_position (is_start ? location->start() : location->end());
3594 MarkerDrag::update_item (Location*)
3599 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3601 _cumulative_x_drag (0),
3602 _cumulative_y_drag (0)
3604 if (_zero_gain_fraction < 0.0) {
3605 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3608 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3610 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3616 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3618 Drag::start_grab (event, _editor->cursors()->fader);
3620 // start the grab at the center of the control point so
3621 // the point doesn't 'jump' to the mouse after the first drag
3622 _fixed_grab_x = _point->get_x();
3623 _fixed_grab_y = _point->get_y();
3625 float const fraction = 1 - (_point->get_y() / _point->line().height());
3627 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3629 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3631 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3633 if (!_point->can_slide ()) {
3634 _x_constrained = true;
3639 ControlPointDrag::motion (GdkEvent* event, bool)
3641 double dx = _drags->current_pointer_x() - last_pointer_x();
3642 double dy = current_pointer_y() - last_pointer_y();
3644 if (event->button.state & Keyboard::SecondaryModifier) {
3649 /* coordinate in pixels relative to the start of the region (for region-based automation)
3650 or track (for track-based automation) */
3651 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3652 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3654 // calculate zero crossing point. back off by .01 to stay on the
3655 // positive side of zero
3656 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3658 // make sure we hit zero when passing through
3659 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3663 if (_x_constrained) {
3666 if (_y_constrained) {
3670 _cumulative_x_drag = cx - _fixed_grab_x;
3671 _cumulative_y_drag = cy - _fixed_grab_y;
3675 cy = min ((double) _point->line().height(), cy);
3677 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3679 if (!_x_constrained) {
3680 _editor->snap_to_with_modifier (cx_frames, event);
3683 cx_frames = min (cx_frames, _point->line().maximum_time());
3685 float const fraction = 1.0 - (cy / _point->line().height());
3687 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3689 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3693 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3695 if (!movement_occurred) {
3699 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3700 _editor->reset_point_selection ();
3704 motion (event, false);
3707 _point->line().end_drag (_pushing, _final_index);
3708 _editor->commit_reversible_command ();
3712 ControlPointDrag::aborted (bool)
3714 _point->line().reset ();
3718 ControlPointDrag::active (Editing::MouseMode m)
3720 if (m == Editing::MouseDraw) {
3721 /* always active in mouse draw */
3725 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3726 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3729 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3732 _cumulative_y_drag (0)
3734 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3738 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3740 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3743 _item = &_line->grab_item ();
3745 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3746 origin, and ditto for y.
3749 double cx = event->button.x;
3750 double cy = event->button.y;
3752 _line->parent_group().canvas_to_item (cx, cy);
3754 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3759 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3760 /* no adjacent points */
3764 Drag::start_grab (event, _editor->cursors()->fader);
3766 /* store grab start in parent frame */
3771 double fraction = 1.0 - (cy / _line->height());
3773 _line->start_drag_line (before, after, fraction);
3775 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3779 LineDrag::motion (GdkEvent* event, bool)
3781 double dy = current_pointer_y() - last_pointer_y();
3783 if (event->button.state & Keyboard::SecondaryModifier) {
3787 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3789 _cumulative_y_drag = cy - _fixed_grab_y;
3792 cy = min ((double) _line->height(), cy);
3794 double const fraction = 1.0 - (cy / _line->height());
3797 /* we are ignoring x position for this drag, so we can just pass in anything */
3798 _line->drag_motion (0, fraction, true, false, ignored);
3800 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3804 LineDrag::finished (GdkEvent* event, bool movement_occured)
3806 if (movement_occured) {
3807 motion (event, false);
3808 _line->end_drag (false, 0);
3810 /* add a new control point on the line */
3812 AutomationTimeAxisView* atv;
3814 _line->end_drag (false, 0);
3816 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3817 framepos_t where = _editor->window_event_sample (event, 0, 0);
3818 atv->add_automation_event (event, where, event->button.y, false);
3822 _editor->commit_reversible_command ();
3826 LineDrag::aborted (bool)
3831 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3834 _cumulative_x_drag (0)
3836 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3840 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3842 Drag::start_grab (event);
3844 _line = reinterpret_cast<Line*> (_item);
3847 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3849 double cx = event->button.x;
3850 double cy = event->button.y;
3852 _item->parent()->canvas_to_item (cx, cy);
3854 /* store grab start in parent frame */
3855 _region_view_grab_x = cx;
3857 _before = *(float*) _item->get_data ("position");
3859 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3861 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3865 FeatureLineDrag::motion (GdkEvent*, bool)
3867 double dx = _drags->current_pointer_x() - last_pointer_x();
3869 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3871 _cumulative_x_drag += dx;
3873 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3882 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3884 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3886 float *pos = new float;
3889 _line->set_data ("position", pos);
3895 FeatureLineDrag::finished (GdkEvent*, bool)
3897 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3898 _arv->update_transient(_before, _before);
3902 FeatureLineDrag::aborted (bool)
3907 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3909 , _vertical_only (false)
3911 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3915 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3917 Drag::start_grab (event);
3918 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
3922 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3929 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
3931 framepos_t grab = grab_frame ();
3932 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
3933 _editor->snap_to_with_modifier (grab, event);
3935 grab = raw_grab_frame ();
3938 /* base start and end on initial click position */
3948 if (current_pointer_y() < grab_y()) {
3949 y1 = current_pointer_y();
3952 y2 = current_pointer_y();
3956 if (start != end || y1 != y2) {
3958 double x1 = _editor->sample_to_pixel (start);
3959 double x2 = _editor->sample_to_pixel (end);
3960 const double min_dimension = 2.0;
3962 if (_vertical_only) {
3963 /* fixed 10 pixel width */
3967 x2 = min (x1 - min_dimension, x2);
3969 x2 = max (x1 + min_dimension, x2);
3974 y2 = min (y1 - min_dimension, y2);
3976 y2 = max (y1 + min_dimension, y2);
3979 /* translate rect into item space and set */
3981 ArdourCanvas::Rect r (x1, y1, x2, y2);
3983 /* this drag is a _trackview_only == true drag, so the y1 and
3984 * y2 (computed using current_pointer_y() and grab_y()) will be
3985 * relative to the top of the trackview group). The
3986 * rubberband rect has the same parent/scroll offset as the
3987 * the trackview group, so we can use the "r" rect directly
3988 * to set the shape of the rubberband.
3991 _editor->rubberband_rect->set (r);
3992 _editor->rubberband_rect->show();
3993 _editor->rubberband_rect->raise_to_top();
3995 show_verbose_cursor_time (pf);
3997 do_select_things (event, true);
4002 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4006 framepos_t grab = grab_frame ();
4007 framepos_t lpf = last_pointer_frame ();
4009 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4010 grab = raw_grab_frame ();
4011 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4025 if (current_pointer_y() < grab_y()) {
4026 y1 = current_pointer_y();
4029 y2 = current_pointer_y();
4033 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4037 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4039 if (movement_occurred) {
4041 motion (event, false);
4042 do_select_things (event, false);
4048 bool do_deselect = true;
4049 MidiTimeAxisView* mtv;
4051 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4053 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4054 /* nothing selected */
4055 add_midi_region (mtv);
4056 do_deselect = false;
4060 /* do not deselect if Primary or Tertiary (toggle-select or
4061 * extend-select are pressed.
4064 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4065 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4072 _editor->rubberband_rect->hide();
4076 RubberbandSelectDrag::aborted (bool)
4078 _editor->rubberband_rect->hide ();
4081 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4082 : RegionDrag (e, i, p, v)
4084 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4088 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4090 Drag::start_grab (event, cursor);
4092 show_verbose_cursor_time (adjusted_current_frame (event));
4096 TimeFXDrag::motion (GdkEvent* event, bool)
4098 RegionView* rv = _primary;
4099 StreamView* cv = rv->get_time_axis_view().view ();
4101 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4102 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4103 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4105 framepos_t const pf = adjusted_current_frame (event);
4107 if (pf > rv->region()->position()) {
4108 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4111 show_verbose_cursor_time (pf);
4115 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4117 _primary->get_time_axis_view().hide_timestretch ();
4119 if (!movement_occurred) {
4123 if (last_pointer_frame() < _primary->region()->position()) {
4124 /* backwards drag of the left edge - not usable */
4128 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4130 float percentage = (double) newlen / (double) _primary->region()->length();
4132 #ifndef USE_RUBBERBAND
4133 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4134 if (_primary->region()->data_type() == DataType::AUDIO) {
4135 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4139 if (!_editor->get_selection().regions.empty()) {
4140 /* primary will already be included in the selection, and edit
4141 group shared editing will propagate selection across
4142 equivalent regions, so just use the current region
4146 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4147 error << _("An error occurred while executing time stretch operation") << endmsg;
4153 TimeFXDrag::aborted (bool)
4155 _primary->get_time_axis_view().hide_timestretch ();
4158 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4161 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4165 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4167 Drag::start_grab (event);
4171 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4173 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4177 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4179 if (movement_occurred && _editor->session()) {
4180 /* make sure we stop */
4181 _editor->session()->request_transport_speed (0.0);
4186 ScrubDrag::aborted (bool)
4191 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4195 , _original_pointer_time_axis (-1)
4196 , _time_selection_at_start (!_editor->get_selection().time.empty())
4198 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4200 if (_time_selection_at_start) {
4201 start_at_start = _editor->get_selection().time.start();
4202 end_at_start = _editor->get_selection().time.end_frame();
4207 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4209 if (_editor->session() == 0) {
4213 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4215 switch (_operation) {
4216 case CreateSelection:
4217 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4222 cursor = _editor->cursors()->selector;
4223 Drag::start_grab (event, cursor);
4226 case SelectionStartTrim:
4227 if (_editor->clicked_axisview) {
4228 _editor->clicked_axisview->order_selection_trims (_item, true);
4230 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4233 case SelectionEndTrim:
4234 if (_editor->clicked_axisview) {
4235 _editor->clicked_axisview->order_selection_trims (_item, false);
4237 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4241 Drag::start_grab (event, cursor);
4244 case SelectionExtend:
4245 Drag::start_grab (event, cursor);
4249 if (_operation == SelectionMove) {
4250 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4252 show_verbose_cursor_time (adjusted_current_frame (event));
4255 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4259 SelectionDrag::setup_pointer_frame_offset ()
4261 switch (_operation) {
4262 case CreateSelection:
4263 _pointer_frame_offset = 0;
4266 case SelectionStartTrim:
4268 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4271 case SelectionEndTrim:
4272 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4275 case SelectionExtend:
4281 SelectionDrag::motion (GdkEvent* event, bool first_move)
4283 framepos_t start = 0;
4285 framecnt_t length = 0;
4286 framecnt_t distance = 0;
4288 framepos_t const pending_position = adjusted_current_frame (event);
4290 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4294 switch (_operation) {
4295 case CreateSelection:
4297 framepos_t grab = grab_frame ();
4300 grab = adjusted_current_frame (event, false);
4301 if (grab < pending_position) {
4302 _editor->snap_to (grab, RoundDownMaybe);
4304 _editor->snap_to (grab, RoundUpMaybe);
4308 if (pending_position < grab) {
4309 start = pending_position;
4312 end = pending_position;
4316 /* first drag: Either add to the selection
4317 or create a new selection
4324 /* adding to the selection */
4325 _editor->set_selected_track_as_side_effect (Selection::Add);
4326 _editor->clicked_selection = _editor->selection->add (start, end);
4333 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4334 _editor->set_selected_track_as_side_effect (Selection::Set);
4337 _editor->clicked_selection = _editor->selection->set (start, end);
4341 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4342 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4343 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4345 _editor->selection->add (atest);
4349 /* select all tracks within the rectangle that we've marked out so far */
4350 TrackViewList new_selection;
4351 TrackViewList& all_tracks (_editor->track_views);
4353 ArdourCanvas::Coord const top = grab_y();
4354 ArdourCanvas::Coord const bottom = current_pointer_y();
4356 if (top >= 0 && bottom >= 0) {
4358 //first, find the tracks that are covered in the y range selection
4359 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4360 if ((*i)->covered_by_y_range (top, bottom)) {
4361 new_selection.push_back (*i);
4365 //now find any tracks that are GROUPED with the tracks we selected
4366 TrackViewList grouped_add = new_selection;
4367 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4368 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4369 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4370 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4371 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4372 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4373 grouped_add.push_back (*j);
4378 //now compare our list with the current selection, and add or remove as necessary
4379 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4380 TrackViewList tracks_to_add;
4381 TrackViewList tracks_to_remove;
4382 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4383 if ( !_editor->selection->tracks.contains ( *i ) )
4384 tracks_to_add.push_back ( *i );
4385 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4386 if ( !grouped_add.contains ( *i ) )
4387 tracks_to_remove.push_back ( *i );
4388 _editor->selection->add(tracks_to_add);
4389 _editor->selection->remove(tracks_to_remove);
4395 case SelectionStartTrim:
4397 start = _editor->selection->time[_editor->clicked_selection].start;
4398 end = _editor->selection->time[_editor->clicked_selection].end;
4400 if (pending_position > end) {
4403 start = pending_position;
4407 case SelectionEndTrim:
4409 start = _editor->selection->time[_editor->clicked_selection].start;
4410 end = _editor->selection->time[_editor->clicked_selection].end;
4412 if (pending_position < start) {
4415 end = pending_position;
4422 start = _editor->selection->time[_editor->clicked_selection].start;
4423 end = _editor->selection->time[_editor->clicked_selection].end;
4425 length = end - start;
4426 distance = pending_position - start;
4427 start = pending_position;
4428 _editor->snap_to (start);
4430 end = start + length;
4434 case SelectionExtend:
4439 switch (_operation) {
4441 if (_time_selection_at_start) {
4442 _editor->selection->move_time (distance);
4446 _editor->selection->replace (_editor->clicked_selection, start, end);
4450 if (_operation == SelectionMove) {
4451 show_verbose_cursor_time(start);
4453 show_verbose_cursor_time(pending_position);
4458 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4460 Session* s = _editor->session();
4462 _editor->begin_reversible_selection_op (_("Change Time Selection"));
4463 if (movement_occurred) {
4464 motion (event, false);
4465 /* XXX this is not object-oriented programming at all. ick */
4466 if (_editor->selection->time.consolidate()) {
4467 _editor->selection->TimeChanged ();
4470 /* XXX what if its a music time selection? */
4472 if ( s->get_play_range() && s->transport_rolling() ) {
4473 s->request_play_range (&_editor->selection->time, true);
4475 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4476 if (_operation == SelectionEndTrim)
4477 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4479 s->request_locate (_editor->get_selection().time.start());
4485 /* just a click, no pointer movement.
4488 if (_operation == SelectionExtend) {
4489 if (_time_selection_at_start) {
4490 framepos_t pos = adjusted_current_frame (event, false);
4491 framepos_t start = min (pos, start_at_start);
4492 framepos_t end = max (pos, end_at_start);
4493 _editor->selection->set (start, end);
4496 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4497 if (_editor->clicked_selection) {
4498 _editor->selection->remove (_editor->clicked_selection);
4501 if (!_editor->clicked_selection) {
4502 _editor->selection->clear_time();
4507 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4508 _editor->selection->set (_editor->clicked_axisview);
4511 if (s && s->get_play_range () && s->transport_rolling()) {
4512 s->request_stop (false, false);
4517 _editor->stop_canvas_autoscroll ();
4518 _editor->clicked_selection = 0;
4519 _editor->commit_reversible_selection_op ();
4523 SelectionDrag::aborted (bool)
4528 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4529 : Drag (e, i, false),
4533 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4535 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4536 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4537 physical_screen_height (_editor->get_window())));
4538 _drag_rect->hide ();
4540 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4541 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4544 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4546 /* normal canvas items will be cleaned up when their parent group is deleted. But
4547 this item is created as the child of a long-lived parent group, and so we
4548 need to explicitly delete it.
4554 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4556 if (_editor->session() == 0) {
4560 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4562 if (!_editor->temp_location) {
4563 _editor->temp_location = new Location (*_editor->session());
4566 switch (_operation) {
4567 case CreateSkipMarker:
4568 case CreateRangeMarker:
4569 case CreateTransportMarker:
4570 case CreateCDMarker:
4572 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4577 cursor = _editor->cursors()->selector;
4581 Drag::start_grab (event, cursor);
4583 show_verbose_cursor_time (adjusted_current_frame (event));
4587 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4589 framepos_t start = 0;
4591 ArdourCanvas::Rectangle *crect;
4593 switch (_operation) {
4594 case CreateSkipMarker:
4595 crect = _editor->range_bar_drag_rect;
4597 case CreateRangeMarker:
4598 crect = _editor->range_bar_drag_rect;
4600 case CreateTransportMarker:
4601 crect = _editor->transport_bar_drag_rect;
4603 case CreateCDMarker:
4604 crect = _editor->cd_marker_bar_drag_rect;
4607 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4612 framepos_t const pf = adjusted_current_frame (event);
4614 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4615 framepos_t grab = grab_frame ();
4616 _editor->snap_to (grab);
4618 if (pf < grab_frame()) {
4626 /* first drag: Either add to the selection
4627 or create a new selection.
4632 _editor->temp_location->set (start, end);
4636 update_item (_editor->temp_location);
4638 //_drag_rect->raise_to_top();
4644 _editor->temp_location->set (start, end);
4646 double x1 = _editor->sample_to_pixel (start);
4647 double x2 = _editor->sample_to_pixel (end);
4651 update_item (_editor->temp_location);
4654 show_verbose_cursor_time (pf);
4659 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4661 Location * newloc = 0;
4665 if (movement_occurred) {
4666 motion (event, false);
4669 switch (_operation) {
4670 case CreateSkipMarker:
4671 case CreateRangeMarker:
4672 case CreateCDMarker:
4674 XMLNode &before = _editor->session()->locations()->get_state();
4675 if (_operation == CreateSkipMarker) {
4676 _editor->begin_reversible_command (_("new skip marker"));
4677 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4678 flags = Location::IsRangeMarker | Location::IsSkip;
4679 _editor->range_bar_drag_rect->hide();
4680 } else if (_operation == CreateCDMarker) {
4681 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4682 _editor->begin_reversible_command (_("new CD marker"));
4683 flags = Location::IsRangeMarker | Location::IsCDMarker;
4684 _editor->cd_marker_bar_drag_rect->hide();
4686 _editor->begin_reversible_command (_("new skip marker"));
4687 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4688 flags = Location::IsRangeMarker;
4689 _editor->range_bar_drag_rect->hide();
4691 newloc = new Location (
4692 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4695 _editor->session()->locations()->add (newloc, true);
4696 XMLNode &after = _editor->session()->locations()->get_state();
4697 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4698 _editor->commit_reversible_command ();
4702 case CreateTransportMarker:
4703 // popup menu to pick loop or punch
4704 _editor->new_transport_marker_context_menu (&event->button, _item);
4710 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4712 if (_operation == CreateTransportMarker) {
4714 /* didn't drag, so just locate */
4716 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4718 } else if (_operation == CreateCDMarker) {
4720 /* didn't drag, but mark is already created so do
4723 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
4729 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4731 if (end == max_framepos) {
4732 end = _editor->session()->current_end_frame ();
4735 if (start == max_framepos) {
4736 start = _editor->session()->current_start_frame ();
4739 switch (_editor->mouse_mode) {
4741 /* find the two markers on either side and then make the selection from it */
4742 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4746 /* find the two markers on either side of the click and make the range out of it */
4747 _editor->selection->set (start, end);
4756 _editor->stop_canvas_autoscroll ();
4760 RangeMarkerBarDrag::aborted (bool movement_occured)
4762 if (movement_occured) {
4763 _drag_rect->hide ();
4768 RangeMarkerBarDrag::update_item (Location* location)
4770 double const x1 = _editor->sample_to_pixel (location->start());
4771 double const x2 = _editor->sample_to_pixel (location->end());
4773 _drag_rect->set_x0 (x1);
4774 _drag_rect->set_x1 (x2);
4777 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4779 , _cumulative_dx (0)
4780 , _cumulative_dy (0)
4782 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4784 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4786 _region = &_primary->region_view ();
4787 _note_height = _region->midi_stream_view()->note_height ();
4791 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4793 Drag::start_grab (event);
4795 if (!(_was_selected = _primary->selected())) {
4797 /* tertiary-click means extend selection - we'll do that on button release,
4798 so don't add it here, because otherwise we make it hard to figure
4799 out the "extend-to" range.
4802 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4805 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4808 _region->note_selected (_primary, true);
4810 _region->unique_select (_primary);
4813 _editor->begin_reversible_selection_op(_("Select Note Press"));
4814 _editor->commit_reversible_selection_op();
4819 /** @return Current total drag x change in frames */
4821 NoteDrag::total_dx () const
4824 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4826 /* primary note time */
4827 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4829 /* new time of the primary note in session frames */
4830 frameoffset_t st = n + dx;
4832 framepos_t const rp = _region->region()->position ();
4834 /* prevent the note being dragged earlier than the region's position */
4837 /* snap and return corresponding delta */
4838 return _region->snap_frame_to_frame (st - rp) + rp - n;
4841 /** @return Current total drag y change in note number */
4843 NoteDrag::total_dy () const
4845 MidiStreamView* msv = _region->midi_stream_view ();
4846 double const y = _region->midi_view()->y_position ();
4847 /* new current note */
4848 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4850 n = max (msv->lowest_note(), n);
4851 n = min (msv->highest_note(), n);
4852 /* and work out delta */
4853 return n - msv->y_to_note (grab_y() - y);
4857 NoteDrag::motion (GdkEvent *, bool)
4859 /* Total change in x and y since the start of the drag */
4860 frameoffset_t const dx = total_dx ();
4861 int8_t const dy = total_dy ();
4863 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4864 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4865 double const tdy = -dy * _note_height - _cumulative_dy;
4868 _cumulative_dx += tdx;
4869 _cumulative_dy += tdy;
4871 int8_t note_delta = total_dy();
4873 _region->move_selection (tdx, tdy, note_delta);
4875 /* the new note value may be the same as the old one, but we
4876 * don't know what that means because the selection may have
4877 * involved more than one note and we might be doing something
4878 * odd with them. so show the note value anyway, always.
4882 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4884 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4885 (int) floor ((double)new_note));
4887 show_verbose_cursor_text (buf);
4892 NoteDrag::finished (GdkEvent* ev, bool moved)
4895 /* no motion - select note */
4897 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4898 _editor->current_mouse_mode() == Editing::MouseDraw) {
4900 bool changed = false;
4902 if (_was_selected) {
4903 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4905 _region->note_deselected (_primary);
4909 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4910 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4912 if (!extend && !add && _region->selection_size() > 1) {
4913 _region->unique_select (_primary);
4915 } else if (extend) {
4916 _region->note_selected (_primary, true, true);
4919 /* it was added during button press */
4924 _editor->begin_reversible_selection_op(_("Select Note Release"));
4925 _editor->commit_reversible_selection_op();
4929 _region->note_dropped (_primary, total_dx(), total_dy());
4934 NoteDrag::aborted (bool)
4939 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4940 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4941 : Drag (editor, atv->base_item ())
4943 , _y_origin (atv->y_position())
4944 , _nothing_to_drag (false)
4946 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4947 setup (atv->lines ());
4950 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
4951 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
4952 : Drag (editor, rv->get_canvas_group ())
4954 , _y_origin (rv->get_time_axis_view().y_position())
4955 , _nothing_to_drag (false)
4958 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4960 list<boost::shared_ptr<AutomationLine> > lines;
4962 AudioRegionView* audio_view;
4963 AutomationRegionView* automation_view;
4964 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
4965 lines.push_back (audio_view->get_gain_line ());
4966 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
4967 lines.push_back (automation_view->line ());
4970 error << _("Automation range drag created for invalid region type") << endmsg;
4976 /** @param lines AutomationLines to drag.
4977 * @param offset Offset from the session start to the points in the AutomationLines.
4980 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4982 /* find the lines that overlap the ranges being dragged */
4983 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4984 while (i != lines.end ()) {
4985 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4988 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4990 /* check this range against all the AudioRanges that we are using */
4991 list<AudioRange>::const_iterator k = _ranges.begin ();
4992 while (k != _ranges.end()) {
4993 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4999 /* add it to our list if it overlaps at all */
5000 if (k != _ranges.end()) {
5005 _lines.push_back (n);
5011 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5015 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5017 return 1.0 - ((global_y - _y_origin) / line->height());
5021 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5023 const double v = list->eval(x);
5024 return _integral ? rint(v) : v;
5028 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5030 Drag::start_grab (event, cursor);
5032 /* Get line states before we start changing things */
5033 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5034 i->state = &i->line->get_state ();
5035 i->original_fraction = y_fraction (i->line, current_pointer_y());
5038 if (_ranges.empty()) {
5040 /* No selected time ranges: drag all points */
5041 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5042 uint32_t const N = i->line->npoints ();
5043 for (uint32_t j = 0; j < N; ++j) {
5044 i->points.push_back (i->line->nth (j));
5050 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5052 framecnt_t const half = (i->start + i->end) / 2;
5054 /* find the line that this audio range starts in */
5055 list<Line>::iterator j = _lines.begin();
5056 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5060 if (j != _lines.end()) {
5061 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5063 /* j is the line that this audio range starts in; fade into it;
5064 64 samples length plucked out of thin air.
5067 framepos_t a = i->start + 64;
5072 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5073 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5075 the_list->editor_add (p, value (the_list, p));
5076 the_list->editor_add (q, value (the_list, q));
5079 /* same thing for the end */
5082 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5086 if (j != _lines.end()) {
5087 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5089 /* j is the line that this audio range starts in; fade out of it;
5090 64 samples length plucked out of thin air.
5093 framepos_t b = i->end - 64;
5098 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5099 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5101 the_list->editor_add (p, value (the_list, p));
5102 the_list->editor_add (q, value (the_list, q));
5106 _nothing_to_drag = true;
5108 /* Find all the points that should be dragged and put them in the relevant
5109 points lists in the Line structs.
5112 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5114 uint32_t const N = i->line->npoints ();
5115 for (uint32_t j = 0; j < N; ++j) {
5117 /* here's a control point on this line */
5118 ControlPoint* p = i->line->nth (j);
5119 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5121 /* see if it's inside a range */
5122 list<AudioRange>::const_iterator k = _ranges.begin ();
5123 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5127 if (k != _ranges.end()) {
5128 /* dragging this point */
5129 _nothing_to_drag = false;
5130 i->points.push_back (p);
5136 if (_nothing_to_drag) {
5140 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5141 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5146 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5148 if (_nothing_to_drag) {
5152 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5153 float const f = y_fraction (l->line, current_pointer_y());
5154 /* we are ignoring x position for this drag, so we can just pass in anything */
5156 l->line->drag_motion (0, f, true, false, ignored);
5157 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5162 AutomationRangeDrag::finished (GdkEvent* event, bool)
5164 if (_nothing_to_drag) {
5168 motion (event, false);
5169 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5170 i->line->end_drag (false, 0);
5173 _editor->commit_reversible_command ();
5177 AutomationRangeDrag::aborted (bool)
5179 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5184 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5186 , initial_time_axis_view (itav)
5188 /* note that time_axis_view may be null if the regionview was created
5189 * as part of a copy operation.
5191 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5192 layer = v->region()->layer ();
5193 initial_y = v->get_canvas_group()->position().y;
5194 initial_playlist = v->region()->playlist ();
5195 initial_position = v->region()->position ();
5196 initial_end = v->region()->position () + v->region()->length ();
5199 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5200 : Drag (e, i->canvas_item ())
5203 , _cumulative_dx (0)
5205 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5206 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5211 PatchChangeDrag::motion (GdkEvent* ev, bool)
5213 framepos_t f = adjusted_current_frame (ev);
5214 boost::shared_ptr<Region> r = _region_view->region ();
5215 f = max (f, r->position ());
5216 f = min (f, r->last_frame ());
5218 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5219 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5220 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5221 _cumulative_dx = dxu;
5225 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5227 if (!movement_occurred) {
5231 boost::shared_ptr<Region> r (_region_view->region ());
5232 framepos_t f = adjusted_current_frame (ev);
5233 f = max (f, r->position ());
5234 f = min (f, r->last_frame ());
5236 _region_view->move_patch_change (
5238 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5243 PatchChangeDrag::aborted (bool)
5245 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5249 PatchChangeDrag::setup_pointer_frame_offset ()
5251 boost::shared_ptr<Region> region = _region_view->region ();
5252 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5255 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5256 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5263 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5265 _region_view->update_drag_selection (
5267 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5271 MidiRubberbandSelectDrag::deselect_things ()
5276 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5277 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5280 _vertical_only = true;
5284 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5286 double const y = _region_view->midi_view()->y_position ();
5288 y1 = max (0.0, y1 - y);
5289 y2 = max (0.0, y2 - y);
5291 _region_view->update_vertical_drag_selection (
5294 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5299 MidiVerticalSelectDrag::deselect_things ()
5304 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5305 : RubberbandSelectDrag (e, i)
5311 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5313 if (drag_in_progress) {
5314 /* We just want to select things at the end of the drag, not during it */
5318 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5320 _editor->begin_reversible_selection_op (_("rubberband selection"));
5322 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5324 _editor->commit_reversible_selection_op ();
5328 EditorRubberbandSelectDrag::deselect_things ()
5330 _editor->begin_reversible_selection_op (_("Clear Selection (rubberband)"));
5332 _editor->selection->clear_tracks();
5333 _editor->selection->clear_regions();
5334 _editor->selection->clear_points ();
5335 _editor->selection->clear_lines ();
5336 _editor->selection->clear_midi_notes ();
5338 _editor->commit_reversible_selection_op();
5341 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5346 _note[0] = _note[1] = 0;
5349 NoteCreateDrag::~NoteCreateDrag ()
5355 NoteCreateDrag::grid_frames (framepos_t t) const
5358 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5360 grid_beats = Evoral::Beats(1);
5363 return _region_view->region_beats_to_region_frames (grid_beats);
5367 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5369 Drag::start_grab (event, cursor);
5371 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5373 framepos_t pf = _drags->current_pointer_frame ();
5374 framecnt_t const g = grid_frames (pf);
5376 /* Hack so that we always snap to the note that we are over, instead of snapping
5377 to the next one if we're more than halfway through the one we're over.
5379 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5383 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5384 _note[1] = _note[0];
5386 MidiStreamView* sv = _region_view->midi_stream_view ();
5387 double const x = _editor->sample_to_pixel (_note[0]);
5388 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5390 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5391 _drag_rect->set_outline_all ();
5392 _drag_rect->set_outline_color (0xffffff99);
5393 _drag_rect->set_fill_color (0xffffff66);
5397 NoteCreateDrag::motion (GdkEvent* event, bool)
5399 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5400 double const x0 = _editor->sample_to_pixel (_note[0]);
5401 double const x1 = _editor->sample_to_pixel (_note[1]);
5402 _drag_rect->set_x0 (std::min(x0, x1));
5403 _drag_rect->set_x1 (std::max(x0, x1));
5407 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5409 if (!had_movement) {
5413 framepos_t const start = min (_note[0], _note[1]);
5414 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5416 framecnt_t const g = grid_frames (start);
5417 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5419 if (_editor->snap_mode() == SnapNormal && length < g) {
5423 Evoral::Beats length_beats = max (
5424 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5426 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5430 NoteCreateDrag::y_to_region (double y) const
5433 _region_view->get_canvas_group()->canvas_to_item (x, y);
5438 NoteCreateDrag::aborted (bool)
5443 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5448 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5452 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5454 Drag::start_grab (event, cursor);
5458 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5464 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5467 distance = _drags->current_pointer_x() - grab_x();
5468 len = ar->fade_in()->back()->when;
5470 distance = grab_x() - _drags->current_pointer_x();
5471 len = ar->fade_out()->back()->when;
5474 /* how long should it be ? */
5476 new_length = len + _editor->pixel_to_sample (distance);
5478 /* now check with the region that this is legal */
5480 new_length = ar->verify_xfade_bounds (new_length, start);
5483 arv->reset_fade_in_shape_width (ar, new_length);
5485 arv->reset_fade_out_shape_width (ar, new_length);
5490 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5496 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5499 distance = _drags->current_pointer_x() - grab_x();
5500 len = ar->fade_in()->back()->when;
5502 distance = grab_x() - _drags->current_pointer_x();
5503 len = ar->fade_out()->back()->when;
5506 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5508 _editor->begin_reversible_command ("xfade trim");
5509 ar->playlist()->clear_owned_changes ();
5512 ar->set_fade_in_length (new_length);
5514 ar->set_fade_out_length (new_length);
5517 /* Adjusting the xfade may affect other regions in the playlist, so we need
5518 to get undo Commands from the whole playlist rather than just the
5522 vector<Command*> cmds;
5523 ar->playlist()->rdiff (cmds);
5524 _editor->session()->add_commands (cmds);
5525 _editor->commit_reversible_command ();
5530 CrossfadeEdgeDrag::aborted (bool)
5533 // arv->redraw_start_xfade ();
5535 // arv->redraw_end_xfade ();
5539 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5540 : Drag (e, item, true)
5541 , line (new EditorCursor (*e))
5543 line->set_position (pos);
5547 RegionCutDrag::~RegionCutDrag ()
5553 RegionCutDrag::motion (GdkEvent*, bool)
5555 framepos_t where = _drags->current_pointer_frame();
5556 _editor->snap_to (where);
5558 line->set_position (where);
5562 RegionCutDrag::finished (GdkEvent*, bool)
5564 _editor->get_track_canvas()->canvas()->re_enter();
5566 framepos_t pos = _drags->current_pointer_frame();
5570 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5576 _editor->split_regions_at (pos, rs);
5580 RegionCutDrag::aborted (bool)