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 , _initially_vertical (false)
225 , _was_double_click (false)
226 , _raw_grab_frame (0)
228 , _last_pointer_frame (0)
234 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
240 _cursor_ctx = CursorContext::create (*_editor, cursor);
242 _cursor_ctx->change (cursor);
249 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
251 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
253 if (Keyboard::is_button2_event (&event->button)) {
254 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
255 _y_constrained = true;
256 _x_constrained = false;
258 _y_constrained = false;
259 _x_constrained = true;
262 _x_constrained = false;
263 _y_constrained = false;
266 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
267 setup_pointer_frame_offset ();
268 _grab_frame = adjusted_frame (_raw_grab_frame, event);
269 _last_pointer_frame = _grab_frame;
270 _last_pointer_x = _grab_x;
272 if (_trackview_only) {
273 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
276 _last_pointer_y = _grab_y;
280 if (!_editor->cursors()->is_invalid (cursor)) {
281 /* CAIROCANVAS need a variant here that passes *cursor */
282 _cursor_ctx = CursorContext::create (*_editor, cursor);
285 if (_editor->session() && _editor->session()->transport_rolling()) {
288 _was_rolling = false;
291 switch (_editor->snap_type()) {
292 case SnapToRegionStart:
293 case SnapToRegionEnd:
294 case SnapToRegionSync:
295 case SnapToRegionBoundary:
296 _editor->build_region_boundary_cache ();
303 /** Call to end a drag `successfully'. Ungrabs item and calls
304 * subclass' finished() method.
306 * @param event GDK event, or 0.
307 * @return true if some movement occurred, otherwise false.
310 Drag::end_grab (GdkEvent* event)
312 _editor->stop_canvas_autoscroll ();
316 finished (event, _move_threshold_passed);
318 _editor->verbose_cursor()->hide ();
321 return _move_threshold_passed;
325 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
329 if (f > _pointer_frame_offset) {
330 pos = f - _pointer_frame_offset;
334 _editor->snap_to_with_modifier (pos, event);
341 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
343 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
347 Drag::current_pointer_x() const
349 return _drags->current_pointer_x ();
353 Drag::current_pointer_y () const
355 if (!_trackview_only) {
356 return _drags->current_pointer_y ();
359 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
363 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
365 /* check to see if we have moved in any way that matters since the last motion event */
366 if (_move_threshold_passed &&
367 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
368 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
372 pair<framecnt_t, int> const threshold = move_threshold ();
374 bool const old_move_threshold_passed = _move_threshold_passed;
376 if (!_move_threshold_passed) {
378 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
379 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
381 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
384 if (active (_editor->mouse_mode) && _move_threshold_passed) {
386 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
388 if (old_move_threshold_passed != _move_threshold_passed) {
392 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
393 _initially_vertical = true;
395 _initially_vertical = false;
398 cerr << "IV = " << _initially_vertical << endl;
401 if (!from_autoscroll) {
402 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
405 if (!_editor->autoscroll_active() || from_autoscroll) {
408 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
410 motion (event, first_move && !_starting_point_passed);
412 if (first_move && !_starting_point_passed) {
413 _starting_point_passed = true;
416 _last_pointer_x = _drags->current_pointer_x ();
417 _last_pointer_y = current_pointer_y ();
418 _last_pointer_frame = adjusted_current_frame (event);
428 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
436 aborted (_move_threshold_passed);
438 _editor->stop_canvas_autoscroll ();
439 _editor->verbose_cursor()->hide ();
443 Drag::show_verbose_cursor_time (framepos_t frame)
445 _editor->verbose_cursor()->set_time (frame);
446 _editor->verbose_cursor()->show ();
450 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
452 _editor->verbose_cursor()->set_duration (start, end);
453 _editor->verbose_cursor()->show ();
457 Drag::show_verbose_cursor_text (string const & text)
459 _editor->verbose_cursor()->set (text);
460 _editor->verbose_cursor()->show ();
463 boost::shared_ptr<Region>
464 Drag::add_midi_region (MidiTimeAxisView* view)
466 if (_editor->session()) {
467 const TempoMap& map (_editor->session()->tempo_map());
468 framecnt_t pos = grab_frame();
469 const Meter& m = map.meter_at (pos);
470 /* not that the frame rate used here can be affected by pull up/down which
473 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
474 return view->add_region (grab_frame(), len, true);
477 return boost::shared_ptr<Region>();
480 struct EditorOrderTimeAxisViewSorter {
481 bool operator() (TimeAxisView* a, TimeAxisView* b) {
482 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
483 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
485 return ra->route()->order_key () < rb->route()->order_key ();
489 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
493 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
495 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
496 as some of the regions we are dragging may be on such tracks.
499 TrackViewList track_views = _editor->track_views;
500 track_views.sort (EditorOrderTimeAxisViewSorter ());
502 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
503 _time_axis_views.push_back (*i);
505 TimeAxisView::Children children_list = (*i)->get_child_list ();
506 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
507 _time_axis_views.push_back (j->get());
511 /* the list of views can be empty at this point if this is a region list-insert drag
514 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
515 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
518 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
522 RegionDrag::region_going_away (RegionView* v)
524 list<DraggingView>::iterator i = _views.begin ();
525 while (i != _views.end() && i->view != v) {
529 if (i != _views.end()) {
534 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
535 * or -1 if it is not found.
538 RegionDrag::find_time_axis_view (TimeAxisView* t) const
541 int const N = _time_axis_views.size ();
542 while (i < N && _time_axis_views[i] != t) {
553 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
554 : RegionDrag (e, i, p, v)
557 , _last_pointer_time_axis_view (0)
558 , _last_pointer_layer (0)
560 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
564 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
566 Drag::start_grab (event, cursor);
568 show_verbose_cursor_time (_last_frame_position);
570 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
572 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
573 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
578 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
580 /* compute the amount of pointer motion in frames, and where
581 the region would be if we moved it by that much.
583 *pending_region_position = adjusted_current_frame (event);
585 framepos_t sync_frame;
586 framecnt_t sync_offset;
589 sync_offset = _primary->region()->sync_offset (sync_dir);
591 /* we don't handle a sync point that lies before zero.
593 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
595 sync_frame = *pending_region_position + (sync_dir*sync_offset);
597 _editor->snap_to_with_modifier (sync_frame, event);
599 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
602 *pending_region_position = _last_frame_position;
605 if (*pending_region_position > max_framepos - _primary->region()->length()) {
606 *pending_region_position = _last_frame_position;
611 /* in locked edit mode, reverse the usual meaning of _x_constrained */
612 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
614 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
616 /* x movement since last time (in pixels) */
617 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
619 /* total x movement */
620 framecnt_t total_dx = *pending_region_position;
621 if (regions_came_from_canvas()) {
622 total_dx = total_dx - grab_frame ();
625 /* check that no regions have gone off the start of the session */
626 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
627 if ((i->view->region()->position() + total_dx) < 0) {
629 *pending_region_position = _last_frame_position;
640 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
642 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
643 int const n = i->time_axis_view + delta_track;
644 if (n < 0 || n >= int (_time_axis_views.size())) {
645 /* off the top or bottom track */
649 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
650 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
651 /* not a track, or the wrong type */
655 double const l = i->layer + delta_layer;
657 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
658 mode to allow the user to place a region below another on layer 0.
660 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
661 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
662 If it has, the layers will be munged later anyway, so it's ok.
668 /* all regions being dragged are ok with this change */
673 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
675 double delta_layer = 0;
676 int delta_time_axis_view = 0;
678 assert (!_views.empty ());
680 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
682 /* Find the TimeAxisView that the pointer is now over */
683 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
684 TimeAxisView* tv = r.first;
686 if (tv && tv->view()) {
687 double layer = r.second;
689 if (first_move && tv->view()->layer_display() == Stacked) {
690 tv->view()->set_layer_display (Expanded);
693 /* Here's the current pointer position in terms of time axis view and layer */
694 int const current_pointer_time_axis_view = find_time_axis_view (tv);
695 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
697 /* Work out the change in y */
699 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
700 delta_layer = current_pointer_layer - _last_pointer_layer;
703 /* Work out the change in x */
704 framepos_t pending_region_position;
705 double const x_delta = compute_x_delta (event, &pending_region_position);
706 _last_frame_position = pending_region_position;
708 /* Verify change in y */
709 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
710 /* this y movement is not allowed, so do no y movement this time */
711 delta_time_axis_view = 0;
715 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
716 /* haven't reached next snap point, and we're not switching
717 trackviews nor layers. nothing to do.
722 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
724 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
726 RegionView* rv = i->view;
731 if (rv->region()->locked() || rv->region()->video_locked()) {
738 /* reparent the regionview into a group above all
742 ArdourCanvas::Item* rvg = rv->get_canvas_group();
743 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
744 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
745 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
746 /* move the item so that it continues to appear at the
747 same location now that its parent has changed.
749 rvg->move (rv_canvas_offset - dmg_canvas_offset);
752 /* If we have moved tracks, we'll fudge the layer delta so that the
753 region gets moved back onto layer 0 on its new track; this avoids
754 confusion when dragging regions from non-zero layers onto different
757 double this_delta_layer = delta_layer;
758 if (delta_time_axis_view != 0) {
759 this_delta_layer = - i->layer;
766 if (i->time_axis_view >= 0) {
767 track_index = i->time_axis_view + delta_time_axis_view;
769 track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
772 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
776 /* The TimeAxisView that this region is now over */
777 TimeAxisView* current_tv = _time_axis_views[track_index];
779 /* Ensure it is moved from stacked -> expanded if appropriate */
780 if (current_tv->view()->layer_display() == Stacked) {
781 current_tv->view()->set_layer_display (Expanded);
784 /* We're only allowed to go -ve in layer on Expanded views */
785 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
786 this_delta_layer = - i->layer;
790 rv->set_height (current_tv->view()->child_height ());
792 /* Update show/hidden status as the region view may have come from a hidden track,
793 or have moved to one.
795 if (current_tv->hidden ()) {
796 rv->get_canvas_group()->hide ();
798 rv->get_canvas_group()->show ();
801 /* Update the DraggingView */
802 i->time_axis_view = track_index;
803 i->layer += this_delta_layer;
806 _editor->mouse_brush_insert_region (rv, pending_region_position);
810 /* Get the y coordinate of the top of the track that this region is now over */
811 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
813 /* And adjust for the layer that it should be on */
814 StreamView* cv = current_tv->view ();
815 switch (cv->layer_display ()) {
819 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
822 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
826 /* need to get the parent of the regionview
827 * canvas group and get its position in
828 * equivalent coordinate space as the trackview
829 * we are now dragging over.
832 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
839 /* Only move the region into the empty dropzone at the bottom if the pointer
843 if (current_pointer_y() >= 0) {
845 Coord last_track_bottom_edge;
846 if (!_time_axis_views.empty()) {
847 TimeAxisView* last = _time_axis_views.back();
848 last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
850 last_track_bottom_edge = 0;
853 y_delta = last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y;
854 i->time_axis_view = -1;
858 /* Now move the region view */
859 rv->move (x_delta, y_delta);
861 } /* foreach region */
863 _total_x_delta += x_delta;
865 if (x_delta != 0 && !_brushing) {
866 show_verbose_cursor_time (_last_frame_position);
869 _last_pointer_time_axis_view += delta_time_axis_view;
870 _last_pointer_layer += delta_layer;
874 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
876 if (_copy && first_move) {
878 if (_x_constrained) {
879 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
881 _editor->begin_reversible_command (Operations::region_copy);
884 /* duplicate the regionview(s) and region(s) */
886 list<DraggingView> new_regionviews;
888 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
890 RegionView* rv = i->view;
891 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
892 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
894 const boost::shared_ptr<const Region> original = rv->region();
895 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
896 region_copy->set_position (original->position());
897 /* need to set this so that the drop zone code can work. This doesn't
898 actually put the region into the playlist, but just sets a weak pointer
901 region_copy->set_playlist (original->playlist());
905 boost::shared_ptr<AudioRegion> audioregion_copy
906 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
908 nrv = new AudioRegionView (*arv, audioregion_copy);
910 boost::shared_ptr<MidiRegion> midiregion_copy
911 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
912 nrv = new MidiRegionView (*mrv, midiregion_copy);
917 nrv->get_canvas_group()->show ();
918 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
920 /* swap _primary to the copy */
922 if (rv == _primary) {
926 /* ..and deselect the one we copied */
928 rv->set_selected (false);
931 if (!new_regionviews.empty()) {
933 /* reflect the fact that we are dragging the copies */
935 _views = new_regionviews;
937 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
940 } else if (!_copy && first_move) {
942 if (_x_constrained) {
943 _editor->begin_reversible_command (_("fixed time region drag"));
945 _editor->begin_reversible_command (Operations::region_drag);
949 RegionMotionDrag::motion (event, first_move);
953 RegionMotionDrag::finished (GdkEvent *, bool)
955 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
960 if ((*i)->view()->layer_display() == Expanded) {
961 (*i)->view()->set_layer_display (Stacked);
967 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
969 RegionMotionDrag::finished (ev, movement_occurred);
971 if (!movement_occurred) {
975 if (was_double_click() && !_views.empty()) {
976 DraggingView dv = _views.front();
977 dv.view->show_region_editor ();
984 /* reverse this here so that we have the correct logic to finalize
988 if (Config->get_edit_mode() == Lock) {
989 _x_constrained = !_x_constrained;
992 assert (!_views.empty ());
994 /* We might have hidden region views so that they weren't visible during the drag
995 (when they have been reparented). Now everything can be shown again, as region
996 views are back in their track parent groups.
998 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
999 i->view->get_canvas_group()->show ();
1002 bool const changed_position = (_last_frame_position != _primary->region()->position());
1003 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1004 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1024 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1028 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1030 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1035 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1036 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1037 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1038 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1040 rtav->set_height (original->current_height());
1044 ChanCount one_midi_port (DataType::MIDI, 1);
1045 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1046 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1047 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1049 rtav->set_height (original->current_height());
1054 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1060 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1062 RegionSelection new_views;
1063 PlaylistSet modified_playlists;
1064 RouteTimeAxisView* new_time_axis_view = 0;
1067 /* all changes were made during motion event handlers */
1069 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1073 _editor->commit_reversible_command ();
1077 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1078 PlaylistMapping playlist_mapping;
1080 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1081 PlaylistMapping playlist_mapping;
1083 /* insert the regions into their new playlists */
1084 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1086 RouteTimeAxisView* dest_rtv = 0;
1088 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1094 if (changed_position && !_x_constrained) {
1095 where = i->view->region()->position() - drag_delta;
1097 where = i->view->region()->position();
1100 if (i->time_axis_view < 0) {
1101 /* dragged to drop zone */
1103 PlaylistMapping::iterator pm;
1105 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1106 /* first region from this original playlist: create a new track */
1107 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1108 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1109 dest_rtv = new_time_axis_view;
1111 /* we already created a new track for regions from this playlist, use it */
1112 dest_rtv = pm->second;
1115 /* destination time axis view is the one we dragged to */
1116 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1119 if (dest_rtv != 0) {
1120 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1121 if (new_view != 0) {
1122 new_views.push_back (new_view);
1126 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1127 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1130 list<DraggingView>::const_iterator next = i;
1136 /* If we've created new regions either by copying or moving
1137 to a new track, we want to replace the old selection with the new ones
1140 if (new_views.size() > 0) {
1141 _editor->selection->set (new_views);
1144 /* write commands for the accumulated diffs for all our modified playlists */
1145 add_stateful_diff_commands_for_playlists (modified_playlists);
1147 _editor->commit_reversible_command ();
1151 RegionMoveDrag::finished_no_copy (
1152 bool const changed_position,
1153 bool const changed_tracks,
1154 framecnt_t const drag_delta
1157 RegionSelection new_views;
1158 PlaylistSet modified_playlists;
1159 PlaylistSet frozen_playlists;
1160 set<RouteTimeAxisView*> views_to_update;
1161 RouteTimeAxisView* new_time_axis_view = 0;
1164 /* all changes were made during motion event handlers */
1165 _editor->commit_reversible_command ();
1169 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1170 PlaylistMapping playlist_mapping;
1172 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1173 PlaylistMapping playlist_mapping;
1175 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1177 RegionView* rv = i->view;
1178 RouteTimeAxisView* dest_rtv = 0;
1180 if (rv->region()->locked() || rv->region()->video_locked()) {
1185 if (i->time_axis_view < 0) {
1186 /* dragged to drop zone */
1188 PlaylistMapping::iterator pm;
1190 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1191 /* first region from this original playlist: create a new track */
1192 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1193 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1194 dest_rtv = new_time_axis_view;
1196 /* we already created a new track for regions from this playlist, use it */
1197 dest_rtv = pm->second;
1201 /* destination time axis view is the one we dragged to */
1202 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1207 double const dest_layer = i->layer;
1209 views_to_update.insert (dest_rtv);
1213 if (changed_position && !_x_constrained) {
1214 where = rv->region()->position() - drag_delta;
1216 where = rv->region()->position();
1219 if (changed_tracks) {
1221 /* insert into new playlist */
1223 RegionView* new_view = insert_region_into_playlist (
1224 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1227 if (new_view == 0) {
1232 new_views.push_back (new_view);
1234 /* remove from old playlist */
1236 /* the region that used to be in the old playlist is not
1237 moved to the new one - we use a copy of it. as a result,
1238 any existing editor for the region should no longer be
1241 rv->hide_region_editor();
1244 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1248 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1250 /* this movement may result in a crossfade being modified, or a layering change,
1251 so we need to get undo data from the playlist as well as the region.
1254 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1256 playlist->clear_changes ();
1259 rv->region()->clear_changes ();
1262 motion on the same track. plonk the previously reparented region
1263 back to its original canvas group (its streamview).
1264 No need to do anything for copies as they are fake regions which will be deleted.
1267 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1268 rv->get_canvas_group()->set_y_position (i->initial_y);
1271 /* just change the model */
1272 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1273 playlist->set_layer (rv->region(), dest_layer);
1276 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1278 r = frozen_playlists.insert (playlist);
1281 playlist->freeze ();
1284 rv->region()->set_position (where);
1286 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1289 if (changed_tracks) {
1291 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1292 was selected in all of them, then removing it from a playlist will have removed all
1293 trace of it from _views (i.e. there were N regions selected, we removed 1,
1294 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1295 corresponding regionview, and _views is now empty).
1297 This could have invalidated any and all iterators into _views.
1299 The heuristic we use here is: if the region selection is empty, break out of the loop
1300 here. if the region selection is not empty, then restart the loop because we know that
1301 we must have removed at least the region(view) we've just been working on as well as any
1302 that we processed on previous iterations.
1304 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1305 we can just iterate.
1309 if (_views.empty()) {
1320 /* If we've created new regions either by copying or moving
1321 to a new track, we want to replace the old selection with the new ones
1324 if (new_views.size() > 0) {
1325 _editor->selection->set (new_views);
1328 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1332 /* write commands for the accumulated diffs for all our modified playlists */
1333 add_stateful_diff_commands_for_playlists (modified_playlists);
1335 _editor->commit_reversible_command ();
1337 /* We have futzed with the layering of canvas items on our streamviews.
1338 If any region changed layer, this will have resulted in the stream
1339 views being asked to set up their region views, and all will be well.
1340 If not, we might now have badly-ordered region views. Ask the StreamViews
1341 involved to sort themselves out, just in case.
1344 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1345 (*i)->view()->playlist_layered ((*i)->track ());
1349 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1350 * @param region Region to remove.
1351 * @param playlist playlist To remove from.
1352 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1353 * that clear_changes () is only called once per playlist.
1356 RegionMoveDrag::remove_region_from_playlist (
1357 boost::shared_ptr<Region> region,
1358 boost::shared_ptr<Playlist> playlist,
1359 PlaylistSet& modified_playlists
1362 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1365 playlist->clear_changes ();
1368 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1372 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1373 * clearing the playlist's diff history first if necessary.
1374 * @param region Region to insert.
1375 * @param dest_rtv Destination RouteTimeAxisView.
1376 * @param dest_layer Destination layer.
1377 * @param where Destination position.
1378 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1379 * that clear_changes () is only called once per playlist.
1380 * @return New RegionView, or 0 if no insert was performed.
1383 RegionMoveDrag::insert_region_into_playlist (
1384 boost::shared_ptr<Region> region,
1385 RouteTimeAxisView* dest_rtv,
1388 PlaylistSet& modified_playlists
1391 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1392 if (!dest_playlist) {
1396 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1397 _new_region_view = 0;
1398 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1400 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1401 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1403 dest_playlist->clear_changes ();
1406 dest_playlist->add_region (region, where);
1408 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1409 dest_playlist->set_layer (region, dest_layer);
1414 assert (_new_region_view);
1416 return _new_region_view;
1420 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1422 _new_region_view = rv;
1426 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1428 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1429 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1431 _editor->session()->add_command (c);
1440 RegionMoveDrag::aborted (bool movement_occurred)
1444 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1445 list<DraggingView>::const_iterator next = i;
1454 RegionMotionDrag::aborted (movement_occurred);
1459 RegionMotionDrag::aborted (bool)
1461 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1463 StreamView* sview = (*i)->view();
1466 if (sview->layer_display() == Expanded) {
1467 sview->set_layer_display (Stacked);
1472 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1473 RegionView* rv = i->view;
1474 TimeAxisView* tv = &(rv->get_time_axis_view ());
1475 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1477 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1478 rv->get_canvas_group()->set_y_position (0);
1480 rv->move (-_total_x_delta, 0);
1481 rv->set_height (rtv->view()->child_height ());
1485 /** @param b true to brush, otherwise false.
1486 * @param c true to make copies of the regions being moved, otherwise false.
1488 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1489 : RegionMotionDrag (e, i, p, v, b)
1492 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1495 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1496 if (rtv && rtv->is_track()) {
1497 speed = rtv->track()->speed ();
1500 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1504 RegionMoveDrag::setup_pointer_frame_offset ()
1506 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1509 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1510 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1512 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1514 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1515 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1517 _primary = v->view()->create_region_view (r, false, false);
1519 _primary->get_canvas_group()->show ();
1520 _primary->set_position (pos, 0);
1521 _views.push_back (DraggingView (_primary, this, v));
1523 _last_frame_position = pos;
1525 _item = _primary->get_canvas_group ();
1529 RegionInsertDrag::finished (GdkEvent *, bool)
1531 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1533 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1534 _primary->get_canvas_group()->set_y_position (0);
1536 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1538 _editor->begin_reversible_command (Operations::insert_region);
1539 playlist->clear_changes ();
1540 playlist->add_region (_primary->region (), _last_frame_position);
1542 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1543 if (Config->get_edit_mode() == Ripple) {
1544 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1547 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1548 _editor->commit_reversible_command ();
1556 RegionInsertDrag::aborted (bool)
1563 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1564 : RegionMoveDrag (e, i, p, v, false, false)
1566 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1569 struct RegionSelectionByPosition {
1570 bool operator() (RegionView*a, RegionView* b) {
1571 return a->region()->position () < b->region()->position();
1576 RegionSpliceDrag::motion (GdkEvent* event, bool)
1578 /* Which trackview is this ? */
1580 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1581 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1583 /* The region motion is only processed if the pointer is over
1587 if (!tv || !tv->is_track()) {
1588 /* To make sure we hide the verbose canvas cursor when the mouse is
1589 not held over an audio track.
1591 _editor->verbose_cursor()->hide ();
1594 _editor->verbose_cursor()->show ();
1599 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1605 RegionSelection copy;
1606 _editor->selection->regions.by_position(copy);
1608 framepos_t const pf = adjusted_current_frame (event);
1610 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1612 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1618 boost::shared_ptr<Playlist> playlist;
1620 if ((playlist = atv->playlist()) == 0) {
1624 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1629 if (pf < (*i)->region()->last_frame() + 1) {
1633 if (pf > (*i)->region()->first_frame()) {
1639 playlist->shuffle ((*i)->region(), dir);
1644 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1646 RegionMoveDrag::finished (event, movement_occurred);
1650 RegionSpliceDrag::aborted (bool)
1660 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1663 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1665 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1666 RegionSelection to_ripple;
1667 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1668 if ((*i)->position() >= where) {
1669 to_ripple.push_back (rtv->view()->find_view(*i));
1673 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1674 if (!exclude.contains (*i)) {
1675 // the selection has already been added to _views
1677 if (drag_in_progress) {
1678 // do the same things that RegionMotionDrag::motion does when
1679 // first_move is true, for the region views that we're adding
1680 // to _views this time
1683 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1684 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1685 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1686 rvg->reparent (_editor->_drag_motion_group);
1688 // we only need to move in the y direction
1689 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1694 _views.push_back (DraggingView (*i, this, tav));
1700 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1703 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1704 // we added all the regions after the selection
1706 std::list<DraggingView>::iterator to_erase = i++;
1707 if (!_editor->selection->regions.contains (to_erase->view)) {
1708 // restore the non-selected regions to their original playlist & positions,
1709 // and then ripple them back by the length of the regions that were dragged away
1710 // do the same things as RegionMotionDrag::aborted
1712 RegionView *rv = to_erase->view;
1713 TimeAxisView* tv = &(rv->get_time_axis_view ());
1714 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1717 // plonk them back onto their own track
1718 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1719 rv->get_canvas_group()->set_y_position (0);
1723 // move the underlying region to match the view
1724 rv->region()->set_position (rv->region()->position() + amount);
1726 // restore the view to match the underlying region's original position
1727 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
1730 rv->set_height (rtv->view()->child_height ());
1731 _views.erase (to_erase);
1737 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const
1739 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) {
1741 return allow_moves_across_tracks;
1749 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1750 : RegionMoveDrag (e, i, p, v, false, false)
1752 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
1753 // compute length of selection
1754 RegionSelection selected_regions = _editor->selection->regions;
1755 selection_length = selected_regions.end_frame() - selected_regions.start();
1757 // we'll only allow dragging to another track in ripple mode if all the regions
1758 // being dragged start off on the same track
1759 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
1762 exclude = new RegionList;
1763 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
1764 exclude->push_back((*i)->region());
1767 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
1768 RegionSelection copy;
1769 selected_regions.by_position(copy); // get selected regions sorted by position into copy
1771 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
1772 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1774 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1775 // find ripple start point on each applicable playlist
1776 RegionView *first_selected_on_this_track = NULL;
1777 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1778 if ((*i)->region()->playlist() == (*pi)) {
1779 // region is on this playlist - it's the first, because they're sorted
1780 first_selected_on_this_track = *i;
1784 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
1785 add_all_after_to_views (
1786 &first_selected_on_this_track->get_time_axis_view(),
1787 first_selected_on_this_track->region()->position(),
1788 selected_regions, false);
1791 if (allow_moves_across_tracks) {
1792 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
1800 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
1802 /* Which trackview is this ? */
1804 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1805 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1807 /* The region motion is only processed if the pointer is over
1811 if (!tv || !tv->is_track()) {
1812 /* To make sure we hide the verbose canvas cursor when the mouse is
1813 not held over an audiotrack.
1815 _editor->verbose_cursor()->hide ();
1819 framepos_t where = adjusted_current_frame (event);
1820 assert (where >= 0);
1822 double delta = compute_x_delta (event, &after);
1824 framecnt_t amount = _editor->pixel_to_sample (delta);
1826 if (allow_moves_across_tracks) {
1827 // all the originally selected regions were on the same track
1829 framecnt_t adjust = 0;
1830 if (prev_tav && tv != prev_tav) {
1831 // dragged onto a different track
1832 // remove the unselected regions from _views, restore them to their original positions
1833 // and add the regions after the drop point on the new playlist to _views instead.
1834 // undo the effect of rippling the previous playlist, and include the effect of removing
1835 // the dragged region(s) from this track
1837 remove_unselected_from_views (prev_amount, false);
1838 // ripple previous playlist according to the regions that have been removed onto the new playlist
1839 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
1842 // move just the selected regions
1843 RegionMoveDrag::motion(event, first_move);
1845 // ensure that the ripple operation on the new playlist inserts selection_length time
1846 adjust = selection_length;
1847 // ripple the new current playlist
1848 tv->playlist()->ripple (where, amount+adjust, exclude);
1850 // add regions after point where drag entered this track to subsequent ripples
1851 add_all_after_to_views (tv, where, _editor->selection->regions, true);
1854 // motion on same track
1855 RegionMoveDrag::motion(event, first_move);
1859 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
1860 prev_position = where;
1862 // selection encompasses multiple tracks - just drag
1863 // cross-track drags are forbidden
1864 RegionMoveDrag::motion(event, first_move);
1867 if (!_x_constrained) {
1868 prev_amount += amount;
1871 _last_frame_position = after;
1875 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
1877 if (!movement_occurred) {
1881 if (was_double_click() && !_views.empty()) {
1882 DraggingView dv = _views.front();
1883 dv.view->show_region_editor ();
1890 _editor->begin_reversible_command(_("Ripple drag"));
1892 // remove the regions being rippled from the dragging view, updating them to
1893 // their new positions
1894 remove_unselected_from_views (prev_amount, true);
1896 if (allow_moves_across_tracks) {
1898 // if regions were dragged across tracks, we've rippled any later
1899 // regions on the track the regions were dragged off, so we need
1900 // to add the original track to the undo record
1901 orig_tav->playlist()->clear_changes();
1902 vector<Command*> cmds;
1903 orig_tav->playlist()->rdiff (cmds);
1904 _editor->session()->add_commands (cmds);
1906 if (prev_tav && prev_tav != orig_tav) {
1907 prev_tav->playlist()->clear_changes();
1908 vector<Command*> cmds;
1909 prev_tav->playlist()->rdiff (cmds);
1910 _editor->session()->add_commands (cmds);
1913 // selection spanned multiple tracks - all will need adding to undo record
1915 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
1916 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1918 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1919 (*pi)->clear_changes();
1920 vector<Command*> cmds;
1921 (*pi)->rdiff (cmds);
1922 _editor->session()->add_commands (cmds);
1926 // other modified playlists are added to undo by RegionMoveDrag::finished()
1927 RegionMoveDrag::finished (event, movement_occurred);
1928 _editor->commit_reversible_command();
1932 RegionRippleDrag::aborted (bool movement_occurred)
1934 RegionMoveDrag::aborted (movement_occurred);
1939 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1941 _view (dynamic_cast<MidiTimeAxisView*> (v))
1943 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1949 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1952 _region = add_midi_region (_view);
1953 _view->playlist()->freeze ();
1956 framepos_t const f = adjusted_current_frame (event);
1957 if (f < grab_frame()) {
1958 _region->set_position (f);
1961 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1962 so that if this region is duplicated, its duplicate starts on
1963 a snap point rather than 1 frame after a snap point. Otherwise things get
1964 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1965 place snapped notes at the start of the region.
1968 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1969 _region->set_length (len < 1 ? 1 : len);
1975 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1977 if (!movement_occurred) {
1978 add_midi_region (_view);
1980 _view->playlist()->thaw ();
1985 RegionCreateDrag::aborted (bool)
1988 _view->playlist()->thaw ();
1994 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1998 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2002 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2004 Gdk::Cursor* cursor;
2005 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2007 float x_fraction = cnote->mouse_x_fraction ();
2009 if (x_fraction > 0.0 && x_fraction < 0.25) {
2010 cursor = _editor->cursors()->left_side_trim;
2013 cursor = _editor->cursors()->right_side_trim;
2017 Drag::start_grab (event, cursor);
2019 region = &cnote->region_view();
2023 if (event->motion.state & Keyboard::PrimaryModifier) {
2029 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2031 if (ms.size() > 1) {
2032 /* has to be relative, may make no sense otherwise */
2036 /* select this note; if it is already selected, preserve the existing selection,
2037 otherwise make this note the only one selected.
2039 region->note_selected (cnote, cnote->selected ());
2041 _editor->begin_reversible_command (_("resize notes"));
2043 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2044 MidiRegionSelection::iterator next;
2047 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2049 mrv->begin_resizing (at_front);
2056 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
2058 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2059 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2060 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2062 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2064 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2070 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
2072 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2073 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2074 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2076 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2078 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2082 _editor->commit_reversible_command ();
2086 NoteResizeDrag::aborted (bool)
2088 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2089 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2090 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2092 mrv->abort_resizing ();
2097 AVDraggingView::AVDraggingView (RegionView* v)
2100 initial_position = v->region()->position ();
2103 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2106 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2109 TrackViewList empty;
2111 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2112 std::list<RegionView*> views = rs.by_layer();
2114 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2115 RegionView* rv = (*i);
2116 if (!rv->region()->video_locked()) {
2119 _views.push_back (AVDraggingView (rv));
2124 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2126 Drag::start_grab (event);
2127 if (_editor->session() == 0) {
2131 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2132 _max_backwards_drag = (
2133 ARDOUR_UI::instance()->video_timeline->get_duration()
2134 + ARDOUR_UI::instance()->video_timeline->get_offset()
2135 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2138 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2139 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2140 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2143 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2146 Timecode::Time timecode;
2147 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2148 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);
2149 show_verbose_cursor_text (buf);
2153 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2155 if (_editor->session() == 0) {
2158 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2162 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2163 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2165 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2166 dt = - _max_backwards_drag;
2169 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2170 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2172 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2173 RegionView* rv = i->view;
2174 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2177 rv->region()->clear_changes ();
2178 rv->region()->suspend_property_changes();
2180 rv->region()->set_position(i->initial_position + dt);
2181 rv->region_changed(ARDOUR::Properties::position);
2184 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2185 Timecode::Time timecode;
2186 Timecode::Time timediff;
2188 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2189 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2190 snprintf (buf, sizeof (buf),
2191 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2192 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2193 , _("Video Start:"),
2194 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2196 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2198 show_verbose_cursor_text (buf);
2202 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2204 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2208 if (!movement_occurred || ! _editor->session()) {
2212 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2214 _editor->begin_reversible_command (_("Move Video"));
2216 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2217 ARDOUR_UI::instance()->video_timeline->save_undo();
2218 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2219 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2221 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2222 i->view->drag_end();
2223 i->view->region()->resume_property_changes ();
2225 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2228 _editor->session()->maybe_update_session_range(
2229 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2230 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2234 _editor->commit_reversible_command ();
2238 VideoTimeLineDrag::aborted (bool)
2240 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2243 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2244 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2246 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2247 i->view->region()->resume_property_changes ();
2248 i->view->region()->set_position(i->initial_position);
2252 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2253 : RegionDrag (e, i, p, v)
2254 , _preserve_fade_anchor (preserve_fade_anchor)
2255 , _jump_position_when_done (false)
2257 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2261 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2264 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2265 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2267 if (tv && tv->is_track()) {
2268 speed = tv->track()->speed();
2271 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2272 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2273 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2275 framepos_t const pf = adjusted_current_frame (event);
2277 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2278 /* Move the contents of the region around without changing the region bounds */
2279 _operation = ContentsTrim;
2280 Drag::start_grab (event, _editor->cursors()->trimmer);
2282 /* These will get overridden for a point trim.*/
2283 if (pf < (region_start + region_length/2)) {
2284 /* closer to front */
2285 _operation = StartTrim;
2287 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2288 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2290 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2294 _operation = EndTrim;
2295 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2296 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2298 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2303 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2304 _jump_position_when_done = true;
2307 switch (_operation) {
2309 show_verbose_cursor_time (region_start);
2310 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2311 i->view->trim_front_starting ();
2315 show_verbose_cursor_time (region_end);
2318 show_verbose_cursor_time (pf);
2322 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2323 i->view->region()->suspend_property_changes ();
2328 TrimDrag::motion (GdkEvent* event, bool first_move)
2330 RegionView* rv = _primary;
2333 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2334 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2335 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2336 frameoffset_t frame_delta = 0;
2338 if (tv && tv->is_track()) {
2339 speed = tv->track()->speed();
2342 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2348 switch (_operation) {
2350 trim_type = "Region start trim";
2353 trim_type = "Region end trim";
2356 trim_type = "Region content trim";
2363 _editor->begin_reversible_command (trim_type);
2365 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2366 RegionView* rv = i->view;
2367 rv->enable_display (false);
2368 rv->region()->playlist()->clear_owned_changes ();
2370 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2373 arv->temporarily_hide_envelope ();
2377 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2378 insert_result = _editor->motion_frozen_playlists.insert (pl);
2380 if (insert_result.second) {
2386 bool non_overlap_trim = false;
2388 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2389 non_overlap_trim = true;
2392 /* contstrain trim to fade length */
2393 if (_preserve_fade_anchor) {
2394 switch (_operation) {
2396 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2397 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2399 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2400 if (ar->locked()) continue;
2401 framecnt_t len = ar->fade_in()->back()->when;
2402 if (len < dt) dt = min(dt, len);
2406 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2407 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2409 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2410 if (ar->locked()) continue;
2411 framecnt_t len = ar->fade_out()->back()->when;
2412 if (len < -dt) dt = max(dt, -len);
2421 switch (_operation) {
2423 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2424 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2425 if (changed && _preserve_fade_anchor) {
2426 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2428 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2429 framecnt_t len = ar->fade_in()->back()->when;
2430 framecnt_t diff = ar->first_frame() - i->initial_position;
2431 framepos_t new_length = len - diff;
2432 i->anchored_fade_length = min (ar->length(), new_length);
2433 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2434 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2441 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2442 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2443 if (changed && _preserve_fade_anchor) {
2444 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2446 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2447 framecnt_t len = ar->fade_out()->back()->when;
2448 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2449 framepos_t new_length = len + diff;
2450 i->anchored_fade_length = min (ar->length(), new_length);
2451 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2452 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2460 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2462 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2463 i->view->move_contents (frame_delta);
2469 switch (_operation) {
2471 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2474 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2477 // show_verbose_cursor_time (frame_delta);
2484 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2486 if (movement_occurred) {
2487 motion (event, false);
2489 if (_operation == StartTrim) {
2490 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2492 /* This must happen before the region's StatefulDiffCommand is created, as it may
2493 `correct' (ahem) the region's _start from being negative to being zero. It
2494 needs to be zero in the undo record.
2496 i->view->trim_front_ending ();
2498 if (_preserve_fade_anchor) {
2499 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2501 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2502 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2503 ar->set_fade_in_length(i->anchored_fade_length);
2504 ar->set_fade_in_active(true);
2507 if (_jump_position_when_done) {
2508 i->view->region()->set_position (i->initial_position);
2511 } else if (_operation == EndTrim) {
2512 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2513 if (_preserve_fade_anchor) {
2514 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2516 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2517 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2518 ar->set_fade_out_length(i->anchored_fade_length);
2519 ar->set_fade_out_active(true);
2522 if (_jump_position_when_done) {
2523 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2528 if (!_views.empty()) {
2529 if (_operation == StartTrim) {
2530 _editor->maybe_locate_with_edit_preroll(
2531 _views.begin()->view->region()->position());
2533 if (_operation == EndTrim) {
2534 _editor->maybe_locate_with_edit_preroll(
2535 _views.begin()->view->region()->position() +
2536 _views.begin()->view->region()->length());
2540 if (!_editor->selection->selected (_primary)) {
2541 _primary->thaw_after_trim ();
2544 set<boost::shared_ptr<Playlist> > diffed_playlists;
2546 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2547 i->view->thaw_after_trim ();
2548 i->view->enable_display (true);
2550 /* Trimming one region may affect others on the playlist, so we need
2551 to get undo Commands from the whole playlist rather than just the
2552 region. Use diffed_playlists to make sure we don't diff a given
2553 playlist more than once.
2555 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2556 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2557 vector<Command*> cmds;
2559 _editor->session()->add_commands (cmds);
2560 diffed_playlists.insert (p);
2565 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2569 _editor->motion_frozen_playlists.clear ();
2570 _editor->commit_reversible_command();
2573 /* no mouse movement */
2574 _editor->point_trim (event, adjusted_current_frame (event));
2577 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2578 if (_operation == StartTrim) {
2579 i->view->trim_front_ending ();
2582 i->view->region()->resume_property_changes ();
2587 TrimDrag::aborted (bool movement_occurred)
2589 /* Our motion method is changing model state, so use the Undo system
2590 to cancel. Perhaps not ideal, as this will leave an Undo point
2591 behind which may be slightly odd from the user's point of view.
2596 if (movement_occurred) {
2600 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2601 i->view->region()->resume_property_changes ();
2606 TrimDrag::setup_pointer_frame_offset ()
2608 list<DraggingView>::iterator i = _views.begin ();
2609 while (i != _views.end() && i->view != _primary) {
2613 if (i == _views.end()) {
2617 switch (_operation) {
2619 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2622 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2629 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2633 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2634 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2639 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2641 Drag::start_grab (event, cursor);
2642 show_verbose_cursor_time (adjusted_current_frame(event));
2646 MeterMarkerDrag::setup_pointer_frame_offset ()
2648 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2652 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2654 if (!_marker->meter().movable()) {
2660 // create a dummy marker for visual representation of moving the
2661 // section, because whether its a copy or not, we're going to
2662 // leave or lose the original marker (leave if its a copy; lose if its
2663 // not, because we'll remove it from the map).
2665 MeterSection section (_marker->meter());
2667 if (!section.movable()) {
2672 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2674 _marker = new MeterMarker (
2676 *_editor->meter_group,
2677 ARDOUR_UI::config()->color ("meter marker"),
2679 *new MeterSection (_marker->meter())
2682 /* use the new marker for the grab */
2683 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2686 TempoMap& map (_editor->session()->tempo_map());
2687 /* get current state */
2688 before_state = &map.get_state();
2689 /* remove the section while we drag it */
2690 map.remove_meter (section, true);
2694 framepos_t const pf = adjusted_current_frame (event);
2696 _marker->set_position (pf);
2697 show_verbose_cursor_time (pf);
2701 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2703 if (!movement_occurred) {
2704 if (was_double_click()) {
2705 _editor->edit_meter_marker (*_marker);
2710 if (!_marker->meter().movable()) {
2714 motion (event, false);
2716 Timecode::BBT_Time when;
2718 TempoMap& map (_editor->session()->tempo_map());
2719 map.bbt_time (last_pointer_frame(), when);
2721 if (_copy == true) {
2722 _editor->begin_reversible_command (_("copy meter mark"));
2723 XMLNode &before = map.get_state();
2724 map.add_meter (_marker->meter(), when);
2725 XMLNode &after = map.get_state();
2726 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2727 _editor->commit_reversible_command ();
2730 _editor->begin_reversible_command (_("move meter mark"));
2732 /* we removed it before, so add it back now */
2734 map.add_meter (_marker->meter(), when);
2735 XMLNode &after = map.get_state();
2736 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2737 _editor->commit_reversible_command ();
2740 // delete the dummy marker we used for visual representation while moving.
2741 // a new visual marker will show up automatically.
2746 MeterMarkerDrag::aborted (bool moved)
2748 _marker->set_position (_marker->meter().frame ());
2751 TempoMap& map (_editor->session()->tempo_map());
2752 /* we removed it before, so add it back now */
2753 map.add_meter (_marker->meter(), _marker->meter().frame());
2754 // delete the dummy marker we used for visual representation while moving.
2755 // a new visual marker will show up automatically.
2760 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2764 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2766 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2771 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2773 Drag::start_grab (event, cursor);
2774 show_verbose_cursor_time (adjusted_current_frame (event));
2778 TempoMarkerDrag::setup_pointer_frame_offset ()
2780 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2784 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2786 if (!_marker->tempo().movable()) {
2792 // create a dummy marker for visual representation of moving the
2793 // section, because whether its a copy or not, we're going to
2794 // leave or lose the original marker (leave if its a copy; lose if its
2795 // not, because we'll remove it from the map).
2797 // create a dummy marker for visual representation of moving the copy.
2798 // The actual copying is not done before we reach the finish callback.
2801 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2803 TempoSection section (_marker->tempo());
2805 _marker = new TempoMarker (
2807 *_editor->tempo_group,
2808 ARDOUR_UI::config()->color ("tempo marker"),
2810 *new TempoSection (_marker->tempo())
2813 /* use the new marker for the grab */
2814 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2817 TempoMap& map (_editor->session()->tempo_map());
2818 /* get current state */
2819 before_state = &map.get_state();
2820 /* remove the section while we drag it */
2821 map.remove_tempo (section, true);
2825 framepos_t const pf = adjusted_current_frame (event);
2826 _marker->set_position (pf);
2827 show_verbose_cursor_time (pf);
2831 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2833 if (!movement_occurred) {
2834 if (was_double_click()) {
2835 _editor->edit_tempo_marker (*_marker);
2840 if (!_marker->tempo().movable()) {
2844 motion (event, false);
2846 TempoMap& map (_editor->session()->tempo_map());
2847 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
2848 Timecode::BBT_Time when;
2850 map.bbt_time (beat_time, when);
2852 if (_copy == true) {
2853 _editor->begin_reversible_command (_("copy tempo mark"));
2854 XMLNode &before = map.get_state();
2855 map.add_tempo (_marker->tempo(), when);
2856 XMLNode &after = map.get_state();
2857 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2858 _editor->commit_reversible_command ();
2861 _editor->begin_reversible_command (_("move tempo mark"));
2862 /* we removed it before, so add it back now */
2863 map.add_tempo (_marker->tempo(), when);
2864 XMLNode &after = map.get_state();
2865 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2866 _editor->commit_reversible_command ();
2869 // delete the dummy marker we used for visual representation while moving.
2870 // a new visual marker will show up automatically.
2875 TempoMarkerDrag::aborted (bool moved)
2877 _marker->set_position (_marker->tempo().frame());
2879 TempoMap& map (_editor->session()->tempo_map());
2880 /* we removed it before, so add it back now */
2881 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2882 // delete the dummy marker we used for visual representation while moving.
2883 // a new visual marker will show up automatically.
2888 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2889 : Drag (e, &c.track_canvas_item(), false)
2893 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2896 /** Do all the things we do when dragging the playhead to make it look as though
2897 * we have located, without actually doing the locate (because that would cause
2898 * the diskstream buffers to be refilled, which is too slow).
2901 CursorDrag::fake_locate (framepos_t t)
2903 _editor->playhead_cursor->set_position (t);
2905 Session* s = _editor->session ();
2906 if (s->timecode_transmission_suspended ()) {
2907 framepos_t const f = _editor->playhead_cursor->current_frame ();
2908 /* This is asynchronous so it will be sent "now"
2910 s->send_mmc_locate (f);
2911 /* These are synchronous and will be sent during the next
2914 s->queue_full_time_code ();
2915 s->queue_song_position_pointer ();
2918 show_verbose_cursor_time (t);
2919 _editor->UpdateAllTransportClocks (t);
2923 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2925 Drag::start_grab (event, c);
2927 _grab_zoom = _editor->samples_per_pixel;
2929 framepos_t where = _editor->canvas_event_sample (event);
2931 _editor->snap_to_with_modifier (where, event);
2933 _editor->_dragging_playhead = true;
2935 Session* s = _editor->session ();
2937 /* grab the track canvas item as well */
2939 _cursor.track_canvas_item().grab();
2942 if (_was_rolling && _stop) {
2946 if (s->is_auditioning()) {
2947 s->cancel_audition ();
2951 if (AudioEngine::instance()->connected()) {
2953 /* do this only if we're the engine is connected
2954 * because otherwise this request will never be
2955 * serviced and we'll busy wait forever. likewise,
2956 * notice if we are disconnected while waiting for the
2957 * request to be serviced.
2960 s->request_suspend_timecode_transmission ();
2961 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2962 /* twiddle our thumbs */
2967 fake_locate (where);
2971 CursorDrag::motion (GdkEvent* event, bool)
2973 framepos_t const adjusted_frame = adjusted_current_frame (event);
2974 if (adjusted_frame != last_pointer_frame()) {
2975 fake_locate (adjusted_frame);
2980 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2982 _editor->_dragging_playhead = false;
2984 _cursor.track_canvas_item().ungrab();
2986 if (!movement_occurred && _stop) {
2990 motion (event, false);
2992 Session* s = _editor->session ();
2994 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2995 _editor->_pending_locate_request = true;
2996 s->request_resume_timecode_transmission ();
3001 CursorDrag::aborted (bool)
3003 _cursor.track_canvas_item().ungrab();
3005 if (_editor->_dragging_playhead) {
3006 _editor->session()->request_resume_timecode_transmission ();
3007 _editor->_dragging_playhead = false;
3010 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3013 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3014 : RegionDrag (e, i, p, v)
3016 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3020 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3022 Drag::start_grab (event, cursor);
3024 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3025 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3027 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3031 FadeInDrag::setup_pointer_frame_offset ()
3033 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3034 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3035 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3039 FadeInDrag::motion (GdkEvent* event, bool)
3041 framecnt_t fade_length;
3043 framepos_t const pos = adjusted_current_frame (event);
3045 boost::shared_ptr<Region> region = _primary->region ();
3047 if (pos < (region->position() + 64)) {
3048 fade_length = 64; // this should be a minimum defined somewhere
3049 } else if (pos > region->last_frame()) {
3050 fade_length = region->length();
3052 fade_length = pos - region->position();
3055 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3057 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3063 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3066 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3070 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3072 if (!movement_occurred) {
3076 framecnt_t fade_length;
3078 framepos_t const pos = adjusted_current_frame (event);
3080 boost::shared_ptr<Region> region = _primary->region ();
3082 if (pos < (region->position() + 64)) {
3083 fade_length = 64; // this should be a minimum defined somewhere
3084 } else if (pos > region->last_frame()) {
3085 fade_length = region->length();
3087 fade_length = pos - region->position();
3090 _editor->begin_reversible_command (_("change fade in length"));
3092 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3094 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3100 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3101 XMLNode &before = alist->get_state();
3103 tmp->audio_region()->set_fade_in_length (fade_length);
3104 tmp->audio_region()->set_fade_in_active (true);
3106 XMLNode &after = alist->get_state();
3107 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3110 _editor->commit_reversible_command ();
3114 FadeInDrag::aborted (bool)
3116 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3117 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3123 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3127 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3128 : RegionDrag (e, i, p, v)
3130 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3134 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3136 Drag::start_grab (event, cursor);
3138 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3139 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3141 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3145 FadeOutDrag::setup_pointer_frame_offset ()
3147 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3148 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3149 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3153 FadeOutDrag::motion (GdkEvent* event, bool)
3155 framecnt_t fade_length;
3157 framepos_t const pos = adjusted_current_frame (event);
3159 boost::shared_ptr<Region> region = _primary->region ();
3161 if (pos > (region->last_frame() - 64)) {
3162 fade_length = 64; // this should really be a minimum fade defined somewhere
3164 else if (pos < region->position()) {
3165 fade_length = region->length();
3168 fade_length = region->last_frame() - pos;
3171 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3173 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3179 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3182 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3186 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3188 if (!movement_occurred) {
3192 framecnt_t fade_length;
3194 framepos_t const pos = adjusted_current_frame (event);
3196 boost::shared_ptr<Region> region = _primary->region ();
3198 if (pos > (region->last_frame() - 64)) {
3199 fade_length = 64; // this should really be a minimum fade defined somewhere
3201 else if (pos < region->position()) {
3202 fade_length = region->length();
3205 fade_length = region->last_frame() - pos;
3208 _editor->begin_reversible_command (_("change fade out length"));
3210 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3212 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3218 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3219 XMLNode &before = alist->get_state();
3221 tmp->audio_region()->set_fade_out_length (fade_length);
3222 tmp->audio_region()->set_fade_out_active (true);
3224 XMLNode &after = alist->get_state();
3225 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3228 _editor->commit_reversible_command ();
3232 FadeOutDrag::aborted (bool)
3234 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3235 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3241 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3245 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3248 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3250 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3253 _points.push_back (ArdourCanvas::Duple (0, 0));
3254 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3257 MarkerDrag::~MarkerDrag ()
3259 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3264 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3266 location = new Location (*l);
3267 markers.push_back (m);
3272 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3274 Drag::start_grab (event, cursor);
3278 Location *location = _editor->find_location_from_marker (_marker, is_start);
3279 _editor->_dragging_edit_point = true;
3281 update_item (location);
3283 // _drag_line->show();
3284 // _line->raise_to_top();
3287 show_verbose_cursor_time (location->start());
3289 show_verbose_cursor_time (location->end());
3292 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3295 case Selection::Toggle:
3296 /* we toggle on the button release */
3298 case Selection::Set:
3299 if (!_editor->selection->selected (_marker)) {
3300 _editor->selection->set (_marker);
3303 case Selection::Extend:
3305 Locations::LocationList ll;
3306 list<Marker*> to_add;
3308 _editor->selection->markers.range (s, e);
3309 s = min (_marker->position(), s);
3310 e = max (_marker->position(), e);
3313 if (e < max_framepos) {
3316 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3317 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3318 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3321 to_add.push_back (lm->start);
3324 to_add.push_back (lm->end);
3328 if (!to_add.empty()) {
3329 _editor->selection->add (to_add);
3333 case Selection::Add:
3334 _editor->selection->add (_marker);
3338 /* Set up copies for us to manipulate during the drag
3341 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3343 Location* l = _editor->find_location_from_marker (*i, is_start);
3350 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3352 /* range: check that the other end of the range isn't
3355 CopiedLocationInfo::iterator x;
3356 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3357 if (*(*x).location == *l) {
3361 if (x == _copied_locations.end()) {
3362 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3364 (*x).markers.push_back (*i);
3365 (*x).move_both = true;
3373 MarkerDrag::setup_pointer_frame_offset ()
3376 Location *location = _editor->find_location_from_marker (_marker, is_start);
3377 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3381 MarkerDrag::motion (GdkEvent* event, bool)
3383 framecnt_t f_delta = 0;
3385 bool move_both = false;
3386 Location *real_location;
3387 Location *copy_location = 0;
3389 framepos_t const newframe = adjusted_current_frame (event);
3390 framepos_t next = newframe;
3392 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3396 CopiedLocationInfo::iterator x;
3398 /* find the marker we're dragging, and compute the delta */
3400 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3402 copy_location = (*x).location;
3404 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3406 /* this marker is represented by this
3407 * CopiedLocationMarkerInfo
3410 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3415 if (real_location->is_mark()) {
3416 f_delta = newframe - copy_location->start();
3420 switch (_marker->type()) {
3421 case Marker::SessionStart:
3422 case Marker::RangeStart:
3423 case Marker::LoopStart:
3424 case Marker::PunchIn:
3425 f_delta = newframe - copy_location->start();
3428 case Marker::SessionEnd:
3429 case Marker::RangeEnd:
3430 case Marker::LoopEnd:
3431 case Marker::PunchOut:
3432 f_delta = newframe - copy_location->end();
3435 /* what kind of marker is this ? */
3444 if (x == _copied_locations.end()) {
3445 /* hmm, impossible - we didn't find the dragged marker */
3449 /* now move them all */
3451 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3453 copy_location = x->location;
3455 if ((type != TrimRight) && (delta < 0)) {
3456 delta = max (-((framecnt_t) earliest_start), delta);
3459 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3463 if (real_location->locked()) {
3467 if (copy_location->is_mark()) {
3471 copy_location->set_start (copy_location->start() + f_delta);
3475 framepos_t new_start = copy_location->start() + f_delta;
3476 framepos_t new_end = copy_location->end() + f_delta;
3478 if (is_start) { // start-of-range marker
3480 if (move_both || (*x).move_both) {
3481 copy_location->set_start (new_start);
3482 copy_location->set_end (new_end);
3483 } else if (new_start < copy_location->end()) {
3484 copy_location->set_start (new_start);
3485 } else if (newframe > 0) {
3486 _editor->snap_to (next, RoundUpAlways, true);
3487 copy_location->set_end (next);
3488 copy_location->set_start (newframe);
3491 } else { // end marker
3493 if (move_both || (*x).move_both) {
3494 copy_location->set_end (new_end);
3495 copy_location->set_start (new_start);
3496 } else if (new_end > copy_location->start()) {
3497 copy_location->set_end (new_end);
3498 } else if (newframe > 0) {
3499 _editor->snap_to (next, RoundDownAlways, true);
3500 copy_location->set_start (next);
3501 copy_location->set_end (newframe);
3506 update_item (copy_location);
3508 /* now lookup the actual GUI items used to display this
3509 * location and move them to wherever the copy of the location
3510 * is now. This means that the logic in ARDOUR::Location is
3511 * still enforced, even though we are not (yet) modifying
3512 * the real Location itself.
3515 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3518 lm->set_position (copy_location->start(), copy_location->end());
3523 assert (!_copied_locations.empty());
3525 show_verbose_cursor_time (newframe);
3529 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3531 if (!movement_occurred) {
3533 if (was_double_click()) {
3534 _editor->rename_marker (_marker);
3538 /* just a click, do nothing but finish
3539 off the selection process
3542 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3545 case Selection::Set:
3546 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3547 _editor->selection->set (_marker);
3551 case Selection::Toggle:
3552 /* we toggle on the button release, click only */
3553 _editor->selection->toggle (_marker);
3556 case Selection::Extend:
3557 case Selection::Add:
3564 _editor->_dragging_edit_point = false;
3566 _editor->begin_reversible_command ( _("move marker") );
3567 XMLNode &before = _editor->session()->locations()->get_state();
3569 MarkerSelection::iterator i;
3570 CopiedLocationInfo::iterator x;
3573 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3574 x != _copied_locations.end() && i != _editor->selection->markers.end();
3577 Location * location = _editor->find_location_from_marker (*i, is_start);
3581 if (location->locked()) {
3585 if (location->is_mark()) {
3586 location->set_start (((*x).location)->start());
3588 location->set (((*x).location)->start(), ((*x).location)->end());
3593 XMLNode &after = _editor->session()->locations()->get_state();
3594 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3595 _editor->commit_reversible_command ();
3599 MarkerDrag::aborted (bool movement_occured)
3601 if (!movement_occured) {
3605 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3607 /* move all markers to their original location */
3610 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3613 Location * location = _editor->find_location_from_marker (*m, is_start);
3616 (*m)->set_position (is_start ? location->start() : location->end());
3623 MarkerDrag::update_item (Location*)
3628 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3630 _cumulative_x_drag (0),
3631 _cumulative_y_drag (0)
3633 if (_zero_gain_fraction < 0.0) {
3634 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3637 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3639 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3645 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3647 Drag::start_grab (event, _editor->cursors()->fader);
3649 // start the grab at the center of the control point so
3650 // the point doesn't 'jump' to the mouse after the first drag
3651 _fixed_grab_x = _point->get_x();
3652 _fixed_grab_y = _point->get_y();
3654 float const fraction = 1 - (_point->get_y() / _point->line().height());
3656 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3658 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3660 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3662 if (!_point->can_slide ()) {
3663 _x_constrained = true;
3668 ControlPointDrag::motion (GdkEvent* event, bool)
3670 double dx = _drags->current_pointer_x() - last_pointer_x();
3671 double dy = current_pointer_y() - last_pointer_y();
3673 if (event->button.state & Keyboard::SecondaryModifier) {
3678 /* coordinate in pixels relative to the start of the region (for region-based automation)
3679 or track (for track-based automation) */
3680 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3681 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3683 // calculate zero crossing point. back off by .01 to stay on the
3684 // positive side of zero
3685 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3687 // make sure we hit zero when passing through
3688 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3692 if (_x_constrained) {
3695 if (_y_constrained) {
3699 _cumulative_x_drag = cx - _fixed_grab_x;
3700 _cumulative_y_drag = cy - _fixed_grab_y;
3704 cy = min ((double) _point->line().height(), cy);
3706 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3708 if (!_x_constrained) {
3709 _editor->snap_to_with_modifier (cx_frames, event);
3712 cx_frames = min (cx_frames, _point->line().maximum_time());
3714 float const fraction = 1.0 - (cy / _point->line().height());
3716 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3718 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3722 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3724 if (!movement_occurred) {
3728 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3729 _editor->reset_point_selection ();
3733 motion (event, false);
3736 _point->line().end_drag (_pushing, _final_index);
3737 _editor->commit_reversible_command ();
3741 ControlPointDrag::aborted (bool)
3743 _point->line().reset ();
3747 ControlPointDrag::active (Editing::MouseMode m)
3749 if (m == Editing::MouseDraw) {
3750 /* always active in mouse draw */
3754 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3755 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3758 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3761 _cumulative_y_drag (0)
3763 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3767 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3769 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3772 _item = &_line->grab_item ();
3774 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3775 origin, and ditto for y.
3778 double cx = event->button.x;
3779 double cy = event->button.y;
3781 _line->parent_group().canvas_to_item (cx, cy);
3783 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3788 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3789 /* no adjacent points */
3793 Drag::start_grab (event, _editor->cursors()->fader);
3795 /* store grab start in parent frame */
3800 double fraction = 1.0 - (cy / _line->height());
3802 _line->start_drag_line (before, after, fraction);
3804 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3808 LineDrag::motion (GdkEvent* event, bool)
3810 double dy = current_pointer_y() - last_pointer_y();
3812 if (event->button.state & Keyboard::SecondaryModifier) {
3816 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3818 _cumulative_y_drag = cy - _fixed_grab_y;
3821 cy = min ((double) _line->height(), cy);
3823 double const fraction = 1.0 - (cy / _line->height());
3826 /* we are ignoring x position for this drag, so we can just pass in anything */
3827 _line->drag_motion (0, fraction, true, false, ignored);
3829 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3833 LineDrag::finished (GdkEvent* event, bool movement_occured)
3835 if (movement_occured) {
3836 motion (event, false);
3837 _line->end_drag (false, 0);
3839 /* add a new control point on the line */
3841 AutomationTimeAxisView* atv;
3843 _line->end_drag (false, 0);
3845 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3846 framepos_t where = _editor->window_event_sample (event, 0, 0);
3847 atv->add_automation_event (event, where, event->button.y, false);
3851 _editor->commit_reversible_command ();
3855 LineDrag::aborted (bool)
3860 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3863 _cumulative_x_drag (0)
3865 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3869 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3871 Drag::start_grab (event);
3873 _line = reinterpret_cast<Line*> (_item);
3876 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3878 double cx = event->button.x;
3879 double cy = event->button.y;
3881 _item->parent()->canvas_to_item (cx, cy);
3883 /* store grab start in parent frame */
3884 _region_view_grab_x = cx;
3886 _before = *(float*) _item->get_data ("position");
3888 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3890 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3894 FeatureLineDrag::motion (GdkEvent*, bool)
3896 double dx = _drags->current_pointer_x() - last_pointer_x();
3898 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3900 _cumulative_x_drag += dx;
3902 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3911 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3913 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3915 float *pos = new float;
3918 _line->set_data ("position", pos);
3924 FeatureLineDrag::finished (GdkEvent*, bool)
3926 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3927 _arv->update_transient(_before, _before);
3931 FeatureLineDrag::aborted (bool)
3936 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3938 , _vertical_only (false)
3940 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3944 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3946 Drag::start_grab (event);
3947 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
3951 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3958 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
3960 framepos_t grab = grab_frame ();
3961 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
3962 _editor->snap_to_with_modifier (grab, event);
3964 grab = raw_grab_frame ();
3967 /* base start and end on initial click position */
3977 if (current_pointer_y() < grab_y()) {
3978 y1 = current_pointer_y();
3981 y2 = current_pointer_y();
3985 if (start != end || y1 != y2) {
3987 double x1 = _editor->sample_to_pixel (start);
3988 double x2 = _editor->sample_to_pixel (end);
3989 const double min_dimension = 2.0;
3991 if (_vertical_only) {
3992 /* fixed 10 pixel width */
3996 x2 = min (x1 - min_dimension, x2);
3998 x2 = max (x1 + min_dimension, x2);
4003 y2 = min (y1 - min_dimension, y2);
4005 y2 = max (y1 + min_dimension, y2);
4008 /* translate rect into item space and set */
4010 ArdourCanvas::Rect r (x1, y1, x2, y2);
4012 /* this drag is a _trackview_only == true drag, so the y1 and
4013 * y2 (computed using current_pointer_y() and grab_y()) will be
4014 * relative to the top of the trackview group). The
4015 * rubberband rect has the same parent/scroll offset as the
4016 * the trackview group, so we can use the "r" rect directly
4017 * to set the shape of the rubberband.
4020 _editor->rubberband_rect->set (r);
4021 _editor->rubberband_rect->show();
4022 _editor->rubberband_rect->raise_to_top();
4024 show_verbose_cursor_time (pf);
4026 do_select_things (event, true);
4031 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4035 framepos_t grab = grab_frame ();
4036 framepos_t lpf = last_pointer_frame ();
4038 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4039 grab = raw_grab_frame ();
4040 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4054 if (current_pointer_y() < grab_y()) {
4055 y1 = current_pointer_y();
4058 y2 = current_pointer_y();
4062 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4066 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4068 if (movement_occurred) {
4070 motion (event, false);
4071 do_select_things (event, false);
4077 bool do_deselect = true;
4078 MidiTimeAxisView* mtv;
4080 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4082 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4083 /* nothing selected */
4084 add_midi_region (mtv);
4085 do_deselect = false;
4089 /* do not deselect if Primary or Tertiary (toggle-select or
4090 * extend-select are pressed.
4093 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4094 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4101 _editor->rubberband_rect->hide();
4105 RubberbandSelectDrag::aborted (bool)
4107 _editor->rubberband_rect->hide ();
4110 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4111 : RegionDrag (e, i, p, v)
4113 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4117 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4119 Drag::start_grab (event, cursor);
4121 show_verbose_cursor_time (adjusted_current_frame (event));
4125 TimeFXDrag::motion (GdkEvent* event, bool)
4127 RegionView* rv = _primary;
4128 StreamView* cv = rv->get_time_axis_view().view ();
4130 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4131 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4132 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4134 framepos_t const pf = adjusted_current_frame (event);
4136 if (pf > rv->region()->position()) {
4137 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4140 show_verbose_cursor_time (pf);
4144 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4146 _primary->get_time_axis_view().hide_timestretch ();
4148 if (!movement_occurred) {
4152 if (last_pointer_frame() < _primary->region()->position()) {
4153 /* backwards drag of the left edge - not usable */
4157 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4159 float percentage = (double) newlen / (double) _primary->region()->length();
4161 #ifndef USE_RUBBERBAND
4162 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4163 if (_primary->region()->data_type() == DataType::AUDIO) {
4164 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4168 if (!_editor->get_selection().regions.empty()) {
4169 /* primary will already be included in the selection, and edit
4170 group shared editing will propagate selection across
4171 equivalent regions, so just use the current region
4175 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4176 error << _("An error occurred while executing time stretch operation") << endmsg;
4182 TimeFXDrag::aborted (bool)
4184 _primary->get_time_axis_view().hide_timestretch ();
4187 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4190 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4194 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4196 Drag::start_grab (event);
4200 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4202 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4206 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4208 if (movement_occurred && _editor->session()) {
4209 /* make sure we stop */
4210 _editor->session()->request_transport_speed (0.0);
4215 ScrubDrag::aborted (bool)
4220 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4224 , _original_pointer_time_axis (-1)
4225 , _time_selection_at_start (!_editor->get_selection().time.empty())
4227 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4229 if (_time_selection_at_start) {
4230 start_at_start = _editor->get_selection().time.start();
4231 end_at_start = _editor->get_selection().time.end_frame();
4236 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4238 if (_editor->session() == 0) {
4242 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4244 switch (_operation) {
4245 case CreateSelection:
4246 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4251 cursor = _editor->cursors()->selector;
4252 Drag::start_grab (event, cursor);
4255 case SelectionStartTrim:
4256 if (_editor->clicked_axisview) {
4257 _editor->clicked_axisview->order_selection_trims (_item, true);
4259 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4262 case SelectionEndTrim:
4263 if (_editor->clicked_axisview) {
4264 _editor->clicked_axisview->order_selection_trims (_item, false);
4266 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4270 Drag::start_grab (event, cursor);
4273 case SelectionExtend:
4274 Drag::start_grab (event, cursor);
4278 if (_operation == SelectionMove) {
4279 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4281 show_verbose_cursor_time (adjusted_current_frame (event));
4284 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4288 SelectionDrag::setup_pointer_frame_offset ()
4290 switch (_operation) {
4291 case CreateSelection:
4292 _pointer_frame_offset = 0;
4295 case SelectionStartTrim:
4297 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4300 case SelectionEndTrim:
4301 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4304 case SelectionExtend:
4310 SelectionDrag::motion (GdkEvent* event, bool first_move)
4312 framepos_t start = 0;
4314 framecnt_t length = 0;
4315 framecnt_t distance = 0;
4317 framepos_t const pending_position = adjusted_current_frame (event);
4319 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4323 switch (_operation) {
4324 case CreateSelection:
4326 framepos_t grab = grab_frame ();
4329 grab = adjusted_current_frame (event, false);
4330 if (grab < pending_position) {
4331 _editor->snap_to (grab, RoundDownMaybe);
4333 _editor->snap_to (grab, RoundUpMaybe);
4337 if (pending_position < grab) {
4338 start = pending_position;
4341 end = pending_position;
4345 /* first drag: Either add to the selection
4346 or create a new selection
4353 /* adding to the selection */
4354 _editor->set_selected_track_as_side_effect (Selection::Add);
4355 _editor->clicked_selection = _editor->selection->add (start, end);
4362 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4363 _editor->set_selected_track_as_side_effect (Selection::Set);
4366 _editor->clicked_selection = _editor->selection->set (start, end);
4370 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4371 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4372 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4374 _editor->selection->add (atest);
4378 /* select all tracks within the rectangle that we've marked out so far */
4379 TrackViewList new_selection;
4380 TrackViewList& all_tracks (_editor->track_views);
4382 ArdourCanvas::Coord const top = grab_y();
4383 ArdourCanvas::Coord const bottom = current_pointer_y();
4385 if (top >= 0 && bottom >= 0) {
4387 //first, find the tracks that are covered in the y range selection
4388 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4389 if ((*i)->covered_by_y_range (top, bottom)) {
4390 new_selection.push_back (*i);
4394 //now find any tracks that are GROUPED with the tracks we selected
4395 TrackViewList grouped_add = new_selection;
4396 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4397 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4398 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4399 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4400 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4401 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4402 grouped_add.push_back (*j);
4407 //now compare our list with the current selection, and add or remove as necessary
4408 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4409 TrackViewList tracks_to_add;
4410 TrackViewList tracks_to_remove;
4411 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4412 if ( !_editor->selection->tracks.contains ( *i ) )
4413 tracks_to_add.push_back ( *i );
4414 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4415 if ( !grouped_add.contains ( *i ) )
4416 tracks_to_remove.push_back ( *i );
4417 _editor->selection->add(tracks_to_add);
4418 _editor->selection->remove(tracks_to_remove);
4424 case SelectionStartTrim:
4426 start = _editor->selection->time[_editor->clicked_selection].start;
4427 end = _editor->selection->time[_editor->clicked_selection].end;
4429 if (pending_position > end) {
4432 start = pending_position;
4436 case SelectionEndTrim:
4438 start = _editor->selection->time[_editor->clicked_selection].start;
4439 end = _editor->selection->time[_editor->clicked_selection].end;
4441 if (pending_position < start) {
4444 end = pending_position;
4451 start = _editor->selection->time[_editor->clicked_selection].start;
4452 end = _editor->selection->time[_editor->clicked_selection].end;
4454 length = end - start;
4455 distance = pending_position - start;
4456 start = pending_position;
4457 _editor->snap_to (start);
4459 end = start + length;
4463 case SelectionExtend:
4468 switch (_operation) {
4470 if (_time_selection_at_start) {
4471 _editor->selection->move_time (distance);
4475 _editor->selection->replace (_editor->clicked_selection, start, end);
4479 if (_operation == SelectionMove) {
4480 show_verbose_cursor_time(start);
4482 show_verbose_cursor_time(pending_position);
4487 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4489 Session* s = _editor->session();
4491 _editor->begin_reversible_selection_op (_("Change Time Selection"));
4492 if (movement_occurred) {
4493 motion (event, false);
4494 /* XXX this is not object-oriented programming at all. ick */
4495 if (_editor->selection->time.consolidate()) {
4496 _editor->selection->TimeChanged ();
4499 /* XXX what if its a music time selection? */
4501 if ( s->get_play_range() && s->transport_rolling() ) {
4502 s->request_play_range (&_editor->selection->time, true);
4504 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4505 if (_operation == SelectionEndTrim)
4506 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4508 s->request_locate (_editor->get_selection().time.start());
4514 /* just a click, no pointer movement.
4517 if (_operation == SelectionExtend) {
4518 if (_time_selection_at_start) {
4519 framepos_t pos = adjusted_current_frame (event, false);
4520 framepos_t start = min (pos, start_at_start);
4521 framepos_t end = max (pos, end_at_start);
4522 _editor->selection->set (start, end);
4525 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4526 if (_editor->clicked_selection) {
4527 _editor->selection->remove (_editor->clicked_selection);
4530 if (!_editor->clicked_selection) {
4531 _editor->selection->clear_time();
4536 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4537 _editor->selection->set (_editor->clicked_axisview);
4540 if (s && s->get_play_range () && s->transport_rolling()) {
4541 s->request_stop (false, false);
4546 _editor->stop_canvas_autoscroll ();
4547 _editor->clicked_selection = 0;
4548 _editor->commit_reversible_selection_op ();
4552 SelectionDrag::aborted (bool)
4557 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4558 : Drag (e, i, false),
4562 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4564 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4565 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4566 physical_screen_height (_editor->get_window())));
4567 _drag_rect->hide ();
4569 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4570 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4573 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4575 /* normal canvas items will be cleaned up when their parent group is deleted. But
4576 this item is created as the child of a long-lived parent group, and so we
4577 need to explicitly delete it.
4583 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4585 if (_editor->session() == 0) {
4589 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4591 if (!_editor->temp_location) {
4592 _editor->temp_location = new Location (*_editor->session());
4595 switch (_operation) {
4596 case CreateSkipMarker:
4597 case CreateRangeMarker:
4598 case CreateTransportMarker:
4599 case CreateCDMarker:
4601 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4606 cursor = _editor->cursors()->selector;
4610 Drag::start_grab (event, cursor);
4612 show_verbose_cursor_time (adjusted_current_frame (event));
4616 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4618 framepos_t start = 0;
4620 ArdourCanvas::Rectangle *crect;
4622 switch (_operation) {
4623 case CreateSkipMarker:
4624 crect = _editor->range_bar_drag_rect;
4626 case CreateRangeMarker:
4627 crect = _editor->range_bar_drag_rect;
4629 case CreateTransportMarker:
4630 crect = _editor->transport_bar_drag_rect;
4632 case CreateCDMarker:
4633 crect = _editor->cd_marker_bar_drag_rect;
4636 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4641 framepos_t const pf = adjusted_current_frame (event);
4643 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4644 framepos_t grab = grab_frame ();
4645 _editor->snap_to (grab);
4647 if (pf < grab_frame()) {
4655 /* first drag: Either add to the selection
4656 or create a new selection.
4661 _editor->temp_location->set (start, end);
4665 update_item (_editor->temp_location);
4667 //_drag_rect->raise_to_top();
4673 _editor->temp_location->set (start, end);
4675 double x1 = _editor->sample_to_pixel (start);
4676 double x2 = _editor->sample_to_pixel (end);
4680 update_item (_editor->temp_location);
4683 show_verbose_cursor_time (pf);
4688 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4690 Location * newloc = 0;
4694 if (movement_occurred) {
4695 motion (event, false);
4698 switch (_operation) {
4699 case CreateSkipMarker:
4700 case CreateRangeMarker:
4701 case CreateCDMarker:
4703 XMLNode &before = _editor->session()->locations()->get_state();
4704 if (_operation == CreateSkipMarker) {
4705 _editor->begin_reversible_command (_("new skip marker"));
4706 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4707 flags = Location::IsRangeMarker | Location::IsSkip;
4708 _editor->range_bar_drag_rect->hide();
4709 } else if (_operation == CreateCDMarker) {
4710 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4711 _editor->begin_reversible_command (_("new CD marker"));
4712 flags = Location::IsRangeMarker | Location::IsCDMarker;
4713 _editor->cd_marker_bar_drag_rect->hide();
4715 _editor->begin_reversible_command (_("new skip marker"));
4716 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4717 flags = Location::IsRangeMarker;
4718 _editor->range_bar_drag_rect->hide();
4720 newloc = new Location (
4721 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4724 _editor->session()->locations()->add (newloc, true);
4725 XMLNode &after = _editor->session()->locations()->get_state();
4726 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4727 _editor->commit_reversible_command ();
4731 case CreateTransportMarker:
4732 // popup menu to pick loop or punch
4733 _editor->new_transport_marker_context_menu (&event->button, _item);
4739 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4741 if (_operation == CreateTransportMarker) {
4743 /* didn't drag, so just locate */
4745 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4747 } else if (_operation == CreateCDMarker) {
4749 /* didn't drag, but mark is already created so do
4752 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
4758 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4760 if (end == max_framepos) {
4761 end = _editor->session()->current_end_frame ();
4764 if (start == max_framepos) {
4765 start = _editor->session()->current_start_frame ();
4768 switch (_editor->mouse_mode) {
4770 /* find the two markers on either side and then make the selection from it */
4771 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4775 /* find the two markers on either side of the click and make the range out of it */
4776 _editor->selection->set (start, end);
4785 _editor->stop_canvas_autoscroll ();
4789 RangeMarkerBarDrag::aborted (bool movement_occured)
4791 if (movement_occured) {
4792 _drag_rect->hide ();
4797 RangeMarkerBarDrag::update_item (Location* location)
4799 double const x1 = _editor->sample_to_pixel (location->start());
4800 double const x2 = _editor->sample_to_pixel (location->end());
4802 _drag_rect->set_x0 (x1);
4803 _drag_rect->set_x1 (x2);
4806 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4808 , _cumulative_dx (0)
4809 , _cumulative_dy (0)
4811 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4813 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4815 _region = &_primary->region_view ();
4816 _note_height = _region->midi_stream_view()->note_height ();
4820 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4822 Drag::start_grab (event);
4824 if (!(_was_selected = _primary->selected())) {
4826 /* tertiary-click means extend selection - we'll do that on button release,
4827 so don't add it here, because otherwise we make it hard to figure
4828 out the "extend-to" range.
4831 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4834 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4837 _region->note_selected (_primary, true);
4839 _region->unique_select (_primary);
4842 _editor->begin_reversible_selection_op(_("Select Note Press"));
4843 _editor->commit_reversible_selection_op();
4848 /** @return Current total drag x change in frames */
4850 NoteDrag::total_dx () const
4853 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4855 /* primary note time */
4856 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4858 /* new time of the primary note in session frames */
4859 frameoffset_t st = n + dx;
4861 framepos_t const rp = _region->region()->position ();
4863 /* prevent the note being dragged earlier than the region's position */
4866 /* snap and return corresponding delta */
4867 return _region->snap_frame_to_frame (st - rp) + rp - n;
4870 /** @return Current total drag y change in note number */
4872 NoteDrag::total_dy () const
4874 MidiStreamView* msv = _region->midi_stream_view ();
4875 double const y = _region->midi_view()->y_position ();
4876 /* new current note */
4877 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4879 n = max (msv->lowest_note(), n);
4880 n = min (msv->highest_note(), n);
4881 /* and work out delta */
4882 return n - msv->y_to_note (grab_y() - y);
4886 NoteDrag::motion (GdkEvent *, bool)
4888 /* Total change in x and y since the start of the drag */
4889 frameoffset_t const dx = total_dx ();
4890 int8_t const dy = total_dy ();
4892 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4893 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4894 double const tdy = -dy * _note_height - _cumulative_dy;
4897 _cumulative_dx += tdx;
4898 _cumulative_dy += tdy;
4900 int8_t note_delta = total_dy();
4902 _region->move_selection (tdx, tdy, note_delta);
4904 /* the new note value may be the same as the old one, but we
4905 * don't know what that means because the selection may have
4906 * involved more than one note and we might be doing something
4907 * odd with them. so show the note value anyway, always.
4911 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4913 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4914 (int) floor ((double)new_note));
4916 show_verbose_cursor_text (buf);
4921 NoteDrag::finished (GdkEvent* ev, bool moved)
4924 /* no motion - select note */
4926 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4927 _editor->current_mouse_mode() == Editing::MouseDraw) {
4929 bool changed = false;
4931 if (_was_selected) {
4932 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4934 _region->note_deselected (_primary);
4938 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4939 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4941 if (!extend && !add && _region->selection_size() > 1) {
4942 _region->unique_select (_primary);
4944 } else if (extend) {
4945 _region->note_selected (_primary, true, true);
4948 /* it was added during button press */
4953 _editor->begin_reversible_selection_op(_("Select Note Release"));
4954 _editor->commit_reversible_selection_op();
4958 _region->note_dropped (_primary, total_dx(), total_dy());
4963 NoteDrag::aborted (bool)
4968 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4969 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4970 : Drag (editor, atv->base_item ())
4972 , _y_origin (atv->y_position())
4973 , _nothing_to_drag (false)
4975 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4976 setup (atv->lines ());
4979 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
4980 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
4981 : Drag (editor, rv->get_canvas_group ())
4983 , _y_origin (rv->get_time_axis_view().y_position())
4984 , _nothing_to_drag (false)
4987 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4989 list<boost::shared_ptr<AutomationLine> > lines;
4991 AudioRegionView* audio_view;
4992 AutomationRegionView* automation_view;
4993 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
4994 lines.push_back (audio_view->get_gain_line ());
4995 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
4996 lines.push_back (automation_view->line ());
4999 error << _("Automation range drag created for invalid region type") << endmsg;
5005 /** @param lines AutomationLines to drag.
5006 * @param offset Offset from the session start to the points in the AutomationLines.
5009 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5011 /* find the lines that overlap the ranges being dragged */
5012 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5013 while (i != lines.end ()) {
5014 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5017 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5019 /* check this range against all the AudioRanges that we are using */
5020 list<AudioRange>::const_iterator k = _ranges.begin ();
5021 while (k != _ranges.end()) {
5022 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5028 /* add it to our list if it overlaps at all */
5029 if (k != _ranges.end()) {
5034 _lines.push_back (n);
5040 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5044 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5046 return 1.0 - ((global_y - _y_origin) / line->height());
5050 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5052 const double v = list->eval(x);
5053 return _integral ? rint(v) : v;
5057 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5059 Drag::start_grab (event, cursor);
5061 /* Get line states before we start changing things */
5062 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5063 i->state = &i->line->get_state ();
5064 i->original_fraction = y_fraction (i->line, current_pointer_y());
5067 if (_ranges.empty()) {
5069 /* No selected time ranges: drag all points */
5070 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5071 uint32_t const N = i->line->npoints ();
5072 for (uint32_t j = 0; j < N; ++j) {
5073 i->points.push_back (i->line->nth (j));
5079 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5081 framecnt_t const half = (i->start + i->end) / 2;
5083 /* find the line that this audio range starts in */
5084 list<Line>::iterator j = _lines.begin();
5085 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5089 if (j != _lines.end()) {
5090 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5092 /* j is the line that this audio range starts in; fade into it;
5093 64 samples length plucked out of thin air.
5096 framepos_t a = i->start + 64;
5101 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5102 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5104 the_list->editor_add (p, value (the_list, p));
5105 the_list->editor_add (q, value (the_list, q));
5108 /* same thing for the end */
5111 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5115 if (j != _lines.end()) {
5116 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5118 /* j is the line that this audio range starts in; fade out of it;
5119 64 samples length plucked out of thin air.
5122 framepos_t b = i->end - 64;
5127 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5128 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5130 the_list->editor_add (p, value (the_list, p));
5131 the_list->editor_add (q, value (the_list, q));
5135 _nothing_to_drag = true;
5137 /* Find all the points that should be dragged and put them in the relevant
5138 points lists in the Line structs.
5141 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5143 uint32_t const N = i->line->npoints ();
5144 for (uint32_t j = 0; j < N; ++j) {
5146 /* here's a control point on this line */
5147 ControlPoint* p = i->line->nth (j);
5148 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5150 /* see if it's inside a range */
5151 list<AudioRange>::const_iterator k = _ranges.begin ();
5152 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5156 if (k != _ranges.end()) {
5157 /* dragging this point */
5158 _nothing_to_drag = false;
5159 i->points.push_back (p);
5165 if (_nothing_to_drag) {
5169 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5170 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5175 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5177 if (_nothing_to_drag) {
5181 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5182 float const f = y_fraction (l->line, current_pointer_y());
5183 /* we are ignoring x position for this drag, so we can just pass in anything */
5185 l->line->drag_motion (0, f, true, false, ignored);
5186 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5191 AutomationRangeDrag::finished (GdkEvent* event, bool)
5193 if (_nothing_to_drag) {
5197 motion (event, false);
5198 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5199 i->line->end_drag (false, 0);
5202 _editor->commit_reversible_command ();
5206 AutomationRangeDrag::aborted (bool)
5208 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5213 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5215 , initial_time_axis_view (itav)
5217 /* note that time_axis_view may be null if the regionview was created
5218 * as part of a copy operation.
5220 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5221 layer = v->region()->layer ();
5222 initial_y = v->get_canvas_group()->position().y;
5223 initial_playlist = v->region()->playlist ();
5224 initial_position = v->region()->position ();
5225 initial_end = v->region()->position () + v->region()->length ();
5228 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5229 : Drag (e, i->canvas_item ())
5232 , _cumulative_dx (0)
5234 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5235 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5240 PatchChangeDrag::motion (GdkEvent* ev, bool)
5242 framepos_t f = adjusted_current_frame (ev);
5243 boost::shared_ptr<Region> r = _region_view->region ();
5244 f = max (f, r->position ());
5245 f = min (f, r->last_frame ());
5247 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5248 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5249 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5250 _cumulative_dx = dxu;
5254 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5256 if (!movement_occurred) {
5260 boost::shared_ptr<Region> r (_region_view->region ());
5261 framepos_t f = adjusted_current_frame (ev);
5262 f = max (f, r->position ());
5263 f = min (f, r->last_frame ());
5265 _region_view->move_patch_change (
5267 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5272 PatchChangeDrag::aborted (bool)
5274 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5278 PatchChangeDrag::setup_pointer_frame_offset ()
5280 boost::shared_ptr<Region> region = _region_view->region ();
5281 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5284 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5285 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5292 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5294 _region_view->update_drag_selection (
5296 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5300 MidiRubberbandSelectDrag::deselect_things ()
5305 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5306 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5309 _vertical_only = true;
5313 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5315 double const y = _region_view->midi_view()->y_position ();
5317 y1 = max (0.0, y1 - y);
5318 y2 = max (0.0, y2 - y);
5320 _region_view->update_vertical_drag_selection (
5323 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5328 MidiVerticalSelectDrag::deselect_things ()
5333 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5334 : RubberbandSelectDrag (e, i)
5340 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5342 if (drag_in_progress) {
5343 /* We just want to select things at the end of the drag, not during it */
5347 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5349 _editor->begin_reversible_selection_op (_("rubberband selection"));
5351 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5353 _editor->commit_reversible_selection_op ();
5357 EditorRubberbandSelectDrag::deselect_things ()
5359 _editor->begin_reversible_selection_op (_("Clear Selection (rubberband)"));
5361 _editor->selection->clear_tracks();
5362 _editor->selection->clear_regions();
5363 _editor->selection->clear_points ();
5364 _editor->selection->clear_lines ();
5365 _editor->selection->clear_midi_notes ();
5367 _editor->commit_reversible_selection_op();
5370 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5375 _note[0] = _note[1] = 0;
5378 NoteCreateDrag::~NoteCreateDrag ()
5384 NoteCreateDrag::grid_frames (framepos_t t) const
5387 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5389 grid_beats = Evoral::Beats(1);
5392 return _region_view->region_beats_to_region_frames (grid_beats);
5396 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5398 Drag::start_grab (event, cursor);
5400 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5402 framepos_t pf = _drags->current_pointer_frame ();
5403 framecnt_t const g = grid_frames (pf);
5405 /* Hack so that we always snap to the note that we are over, instead of snapping
5406 to the next one if we're more than halfway through the one we're over.
5408 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5412 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5413 _note[1] = _note[0];
5415 MidiStreamView* sv = _region_view->midi_stream_view ();
5416 double const x = _editor->sample_to_pixel (_note[0]);
5417 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5419 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5420 _drag_rect->set_outline_all ();
5421 _drag_rect->set_outline_color (0xffffff99);
5422 _drag_rect->set_fill_color (0xffffff66);
5426 NoteCreateDrag::motion (GdkEvent* event, bool)
5428 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5429 double const x0 = _editor->sample_to_pixel (_note[0]);
5430 double const x1 = _editor->sample_to_pixel (_note[1]);
5431 _drag_rect->set_x0 (std::min(x0, x1));
5432 _drag_rect->set_x1 (std::max(x0, x1));
5436 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5438 if (!had_movement) {
5442 framepos_t const start = min (_note[0], _note[1]);
5443 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5445 framecnt_t const g = grid_frames (start);
5446 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5448 if (_editor->snap_mode() == SnapNormal && length < g) {
5452 Evoral::Beats length_beats = max (
5453 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5455 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5459 NoteCreateDrag::y_to_region (double y) const
5462 _region_view->get_canvas_group()->canvas_to_item (x, y);
5467 NoteCreateDrag::aborted (bool)
5472 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5477 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5481 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5483 Drag::start_grab (event, cursor);
5487 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5493 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5496 distance = _drags->current_pointer_x() - grab_x();
5497 len = ar->fade_in()->back()->when;
5499 distance = grab_x() - _drags->current_pointer_x();
5500 len = ar->fade_out()->back()->when;
5503 /* how long should it be ? */
5505 new_length = len + _editor->pixel_to_sample (distance);
5507 /* now check with the region that this is legal */
5509 new_length = ar->verify_xfade_bounds (new_length, start);
5512 arv->reset_fade_in_shape_width (ar, new_length);
5514 arv->reset_fade_out_shape_width (ar, new_length);
5519 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5525 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5528 distance = _drags->current_pointer_x() - grab_x();
5529 len = ar->fade_in()->back()->when;
5531 distance = grab_x() - _drags->current_pointer_x();
5532 len = ar->fade_out()->back()->when;
5535 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5537 _editor->begin_reversible_command ("xfade trim");
5538 ar->playlist()->clear_owned_changes ();
5541 ar->set_fade_in_length (new_length);
5543 ar->set_fade_out_length (new_length);
5546 /* Adjusting the xfade may affect other regions in the playlist, so we need
5547 to get undo Commands from the whole playlist rather than just the
5551 vector<Command*> cmds;
5552 ar->playlist()->rdiff (cmds);
5553 _editor->session()->add_commands (cmds);
5554 _editor->commit_reversible_command ();
5559 CrossfadeEdgeDrag::aborted (bool)
5562 // arv->redraw_start_xfade ();
5564 // arv->redraw_end_xfade ();
5568 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5569 : Drag (e, item, true)
5570 , line (new EditorCursor (*e))
5572 line->set_position (pos);
5576 RegionCutDrag::~RegionCutDrag ()
5582 RegionCutDrag::motion (GdkEvent*, bool)
5584 framepos_t where = _drags->current_pointer_frame();
5585 _editor->snap_to (where);
5587 line->set_position (where);
5591 RegionCutDrag::finished (GdkEvent*, bool)
5593 _editor->get_track_canvas()->canvas()->re_enter();
5595 framepos_t pos = _drags->current_pointer_frame();
5599 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5605 _editor->split_regions_at (pos, rs);
5609 RegionCutDrag::aborted (bool)