2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
49 #include "audio_region_view.h"
50 #include "automation_region_view.h"
51 #include "midi_region_view.h"
52 #include "ardour_ui.h"
53 #include "gui_thread.h"
54 #include "control_point.h"
55 #include "region_gain_line.h"
56 #include "editor_drag.h"
57 #include "audio_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "selection.h"
60 #include "midi_selection.h"
61 #include "automation_time_axis.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "verbose_cursor.h"
70 using namespace ARDOUR;
73 using namespace Gtkmm2ext;
74 using namespace Editing;
75 using namespace ArdourCanvas;
77 using Gtkmm2ext::Keyboard;
79 double ControlPointDrag::_zero_gain_fraction = -1.0;
81 DragManager::DragManager (Editor* e)
84 , _current_pointer_frame (0)
88 DragManager::~DragManager ()
93 /** Call abort for each active drag */
99 cerr << "Aborting drag\n";
101 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
106 if (!_drags.empty ()) {
107 _editor->set_follow_playhead (_old_follow_playhead, false);
111 _editor->abort_reversible_command();
117 DragManager::add (Drag* d)
119 d->set_manager (this);
120 _drags.push_back (d);
124 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
126 d->set_manager (this);
127 _drags.push_back (d);
132 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
134 /* Prevent follow playhead during the drag to be nice to the user */
135 _old_follow_playhead = _editor->follow_playhead ();
136 _editor->set_follow_playhead (false);
138 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
140 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
141 (*i)->start_grab (e, c);
145 /** Call end_grab for each active drag.
146 * @return true if any drag reported movement having occurred.
149 DragManager::end_grab (GdkEvent* e)
154 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
155 bool const t = (*i)->end_grab (e);
166 _editor->set_follow_playhead (_old_follow_playhead, false);
172 DragManager::mark_double_click ()
174 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
175 (*i)->set_double_click (true);
180 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
184 /* calling this implies that we expect the event to have canvas
187 * Can we guarantee that this is true?
190 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
192 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
193 bool const t = (*i)->motion_handler (e, from_autoscroll);
194 /* run all handlers; return true if at least one of them
195 returns true (indicating that the event has been handled).
207 DragManager::have_item (ArdourCanvas::Item* i) const
209 list<Drag*>::const_iterator j = _drags.begin ();
210 while (j != _drags.end() && (*j)->item () != i) {
214 return j != _drags.end ();
217 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
220 , _pointer_frame_offset (0)
221 , _trackview_only (trackview_only)
222 , _move_threshold_passed (false)
223 , _starting_point_passed (false)
224 , _was_double_click (false)
225 , _raw_grab_frame (0)
227 , _last_pointer_frame (0)
233 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
239 _cursor_ctx = CursorContext::create (*_editor, cursor);
241 _cursor_ctx->change (cursor);
248 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
250 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
252 if (Keyboard::is_button2_event (&event->button)) {
253 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
254 _y_constrained = true;
255 _x_constrained = false;
257 _y_constrained = false;
258 _x_constrained = true;
261 _x_constrained = false;
262 _y_constrained = false;
265 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
266 setup_pointer_frame_offset ();
267 _grab_frame = adjusted_frame (_raw_grab_frame, event);
268 _last_pointer_frame = _grab_frame;
269 _last_pointer_x = _grab_x;
271 if (_trackview_only) {
272 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
275 _last_pointer_y = _grab_y;
279 if (!_editor->cursors()->is_invalid (cursor)) {
280 /* CAIROCANVAS need a variant here that passes *cursor */
281 _cursor_ctx = CursorContext::create (*_editor, cursor);
284 if (_editor->session() && _editor->session()->transport_rolling()) {
287 _was_rolling = false;
290 switch (_editor->snap_type()) {
291 case SnapToRegionStart:
292 case SnapToRegionEnd:
293 case SnapToRegionSync:
294 case SnapToRegionBoundary:
295 _editor->build_region_boundary_cache ();
302 /** Call to end a drag `successfully'. Ungrabs item and calls
303 * subclass' finished() method.
305 * @param event GDK event, or 0.
306 * @return true if some movement occurred, otherwise false.
309 Drag::end_grab (GdkEvent* event)
311 _editor->stop_canvas_autoscroll ();
315 finished (event, _move_threshold_passed);
317 _editor->verbose_cursor()->hide ();
320 return _move_threshold_passed;
324 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
328 if (f > _pointer_frame_offset) {
329 pos = f - _pointer_frame_offset;
333 _editor->snap_to_with_modifier (pos, event);
340 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
342 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
346 Drag::current_pointer_x() const
348 return _drags->current_pointer_x ();
352 Drag::current_pointer_y () const
354 if (!_trackview_only) {
355 return _drags->current_pointer_y ();
358 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
362 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
364 /* check to see if we have moved in any way that matters since the last motion event */
365 if (_move_threshold_passed &&
366 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
367 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
371 pair<framecnt_t, int> const threshold = move_threshold ();
373 bool const old_move_threshold_passed = _move_threshold_passed;
375 if (!_move_threshold_passed) {
377 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
378 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
380 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
383 if (active (_editor->mouse_mode) && _move_threshold_passed) {
385 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
386 if (!from_autoscroll) {
387 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
390 if (!_editor->autoscroll_active() || from_autoscroll) {
392 bool first_move = (_move_threshold_passed != old_move_threshold_passed) ||
394 motion (event, first_move && !_starting_point_passed);
396 if (first_move && !_starting_point_passed) {
397 _starting_point_passed = true;
400 _last_pointer_x = _drags->current_pointer_x ();
401 _last_pointer_y = current_pointer_y ();
402 _last_pointer_frame = adjusted_current_frame (event);
412 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
420 aborted (_move_threshold_passed);
422 _editor->stop_canvas_autoscroll ();
423 _editor->verbose_cursor()->hide ();
427 Drag::show_verbose_cursor_time (framepos_t frame)
429 _editor->verbose_cursor()->set_time (frame);
430 _editor->verbose_cursor()->show ();
434 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
436 _editor->verbose_cursor()->set_duration (start, end);
437 _editor->verbose_cursor()->show ();
441 Drag::show_verbose_cursor_text (string const & text)
443 _editor->verbose_cursor()->set (text);
444 _editor->verbose_cursor()->show ();
447 boost::shared_ptr<Region>
448 Drag::add_midi_region (MidiTimeAxisView* view)
450 if (_editor->session()) {
451 const TempoMap& map (_editor->session()->tempo_map());
452 framecnt_t pos = grab_frame();
453 const Meter& m = map.meter_at (pos);
454 /* not that the frame rate used here can be affected by pull up/down which
457 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
458 return view->add_region (grab_frame(), len, true);
461 return boost::shared_ptr<Region>();
464 struct EditorOrderTimeAxisViewSorter {
465 bool operator() (TimeAxisView* a, TimeAxisView* b) {
466 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
467 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
469 return ra->route()->order_key () < rb->route()->order_key ();
473 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
477 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
479 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
480 as some of the regions we are dragging may be on such tracks.
483 TrackViewList track_views = _editor->track_views;
484 track_views.sort (EditorOrderTimeAxisViewSorter ());
486 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
487 _time_axis_views.push_back (*i);
489 TimeAxisView::Children children_list = (*i)->get_child_list ();
490 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
491 _time_axis_views.push_back (j->get());
495 /* the list of views can be empty at this point if this is a region list-insert drag
498 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
499 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
502 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
506 RegionDrag::region_going_away (RegionView* v)
508 list<DraggingView>::iterator i = _views.begin ();
509 while (i != _views.end() && i->view != v) {
513 if (i != _views.end()) {
518 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
519 * or -1 if it is not found.
522 RegionDrag::find_time_axis_view (TimeAxisView* t) const
525 int const N = _time_axis_views.size ();
526 while (i < N && _time_axis_views[i] != t) {
537 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
538 : RegionDrag (e, i, p, v)
541 , _last_pointer_time_axis_view (0)
542 , _last_pointer_layer (0)
544 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
548 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
550 Drag::start_grab (event, cursor);
552 show_verbose_cursor_time (_last_frame_position);
554 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
556 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
557 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
562 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
564 /* compute the amount of pointer motion in frames, and where
565 the region would be if we moved it by that much.
567 *pending_region_position = adjusted_current_frame (event);
569 framepos_t sync_frame;
570 framecnt_t sync_offset;
573 sync_offset = _primary->region()->sync_offset (sync_dir);
575 /* we don't handle a sync point that lies before zero.
577 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
579 sync_frame = *pending_region_position + (sync_dir*sync_offset);
581 _editor->snap_to_with_modifier (sync_frame, event);
583 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
586 *pending_region_position = _last_frame_position;
589 if (*pending_region_position > max_framepos - _primary->region()->length()) {
590 *pending_region_position = _last_frame_position;
595 /* in locked edit mode, reverse the usual meaning of _x_constrained */
596 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
598 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
600 /* x movement since last time (in pixels) */
601 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
603 /* total x movement */
604 framecnt_t total_dx = *pending_region_position;
605 if (regions_came_from_canvas()) {
606 total_dx = total_dx - grab_frame ();
609 /* check that no regions have gone off the start of the session */
610 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
611 if ((i->view->region()->position() + total_dx) < 0) {
613 *pending_region_position = _last_frame_position;
624 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
626 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
627 int const n = i->time_axis_view + delta_track;
628 if (n < 0 || n >= int (_time_axis_views.size())) {
629 /* off the top or bottom track */
633 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
634 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
635 /* not a track, or the wrong type */
639 double const l = i->layer + delta_layer;
641 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
642 mode to allow the user to place a region below another on layer 0.
644 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
645 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
646 If it has, the layers will be munged later anyway, so it's ok.
652 /* all regions being dragged are ok with this change */
657 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
659 double delta_layer = 0;
660 int delta_time_axis_view = 0;
662 assert (!_views.empty ());
664 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
666 /* Find the TimeAxisView that the pointer is now over */
667 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
668 TimeAxisView* tv = r.first;
670 if (tv && tv->view()) {
671 double layer = r.second;
673 if (first_move && tv->view()->layer_display() == Stacked) {
674 tv->view()->set_layer_display (Expanded);
677 /* Here's the current pointer position in terms of time axis view and layer */
678 int const current_pointer_time_axis_view = find_time_axis_view (tv);
679 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
681 /* Work out the change in y */
683 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
684 delta_layer = current_pointer_layer - _last_pointer_layer;
687 /* Work out the change in x */
688 framepos_t pending_region_position;
689 double const x_delta = compute_x_delta (event, &pending_region_position);
690 _last_frame_position = pending_region_position;
692 /* Verify change in y */
693 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
694 /* this y movement is not allowed, so do no y movement this time */
695 delta_time_axis_view = 0;
699 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
700 /* haven't reached next snap point, and we're not switching
701 trackviews nor layers. nothing to do.
706 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
708 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
710 RegionView* rv = i->view;
715 if (rv->region()->locked() || rv->region()->video_locked()) {
722 /* reparent the regionview into a group above all
726 ArdourCanvas::Item* rvg = rv->get_canvas_group();
727 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
728 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
729 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
730 /* move the item so that it continues to appear at the
731 same location now that its parent has changed.
733 rvg->move (rv_canvas_offset - dmg_canvas_offset);
736 /* If we have moved tracks, we'll fudge the layer delta so that the
737 region gets moved back onto layer 0 on its new track; this avoids
738 confusion when dragging regions from non-zero layers onto different
741 double this_delta_layer = delta_layer;
742 if (delta_time_axis_view != 0) {
743 this_delta_layer = - i->layer;
750 if (i->time_axis_view >= 0) {
751 track_index = i->time_axis_view + delta_time_axis_view;
753 track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
756 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
760 /* The TimeAxisView that this region is now over */
761 TimeAxisView* current_tv = _time_axis_views[track_index];
763 /* Ensure it is moved from stacked -> expanded if appropriate */
764 if (current_tv->view()->layer_display() == Stacked) {
765 current_tv->view()->set_layer_display (Expanded);
768 /* We're only allowed to go -ve in layer on Expanded views */
769 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
770 this_delta_layer = - i->layer;
774 rv->set_height (current_tv->view()->child_height ());
776 /* Update show/hidden status as the region view may have come from a hidden track,
777 or have moved to one.
779 if (current_tv->hidden ()) {
780 rv->get_canvas_group()->hide ();
782 rv->get_canvas_group()->show ();
785 /* Update the DraggingView */
786 i->time_axis_view = track_index;
787 i->layer += this_delta_layer;
790 _editor->mouse_brush_insert_region (rv, pending_region_position);
794 /* Get the y coordinate of the top of the track that this region is now over */
795 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
797 /* And adjust for the layer that it should be on */
798 StreamView* cv = current_tv->view ();
799 switch (cv->layer_display ()) {
803 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
806 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
810 /* need to get the parent of the regionview
811 * canvas group and get its position in
812 * equivalent coordinate space as the trackview
813 * we are now dragging over.
816 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
823 /* Only move the region into the empty dropzone at the bottom if the pointer
827 if (current_pointer_y() >= 0) {
829 Coord last_track_bottom_edge;
830 if (!_time_axis_views.empty()) {
831 TimeAxisView* last = _time_axis_views.back();
832 last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
834 last_track_bottom_edge = 0;
837 y_delta = last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y;
838 i->time_axis_view = -1;
842 /* Now move the region view */
843 rv->move (x_delta, y_delta);
845 } /* foreach region */
847 _total_x_delta += x_delta;
849 if (x_delta != 0 && !_brushing) {
850 show_verbose_cursor_time (_last_frame_position);
853 _last_pointer_time_axis_view += delta_time_axis_view;
854 _last_pointer_layer += delta_layer;
858 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
860 if (_copy && first_move) {
862 if (_x_constrained) {
863 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
865 _editor->begin_reversible_command (Operations::region_copy);
868 /* duplicate the regionview(s) and region(s) */
870 list<DraggingView> new_regionviews;
872 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
874 RegionView* rv = i->view;
875 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
876 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
878 const boost::shared_ptr<const Region> original = rv->region();
879 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
880 region_copy->set_position (original->position());
884 boost::shared_ptr<AudioRegion> audioregion_copy
885 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
887 nrv = new AudioRegionView (*arv, audioregion_copy);
889 boost::shared_ptr<MidiRegion> midiregion_copy
890 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
891 nrv = new MidiRegionView (*mrv, midiregion_copy);
896 nrv->get_canvas_group()->show ();
897 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
899 /* swap _primary to the copy */
901 if (rv == _primary) {
905 /* ..and deselect the one we copied */
907 rv->set_selected (false);
910 if (!new_regionviews.empty()) {
912 /* reflect the fact that we are dragging the copies */
914 _views = new_regionviews;
916 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
919 } else if (!_copy && first_move) {
921 if (_x_constrained) {
922 _editor->begin_reversible_command (_("fixed time region drag"));
924 _editor->begin_reversible_command (Operations::region_drag);
928 RegionMotionDrag::motion (event, first_move);
932 RegionMotionDrag::finished (GdkEvent *, bool)
934 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
939 if ((*i)->view()->layer_display() == Expanded) {
940 (*i)->view()->set_layer_display (Stacked);
946 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
948 RegionMotionDrag::finished (ev, movement_occurred);
950 if (!movement_occurred) {
954 if (was_double_click() && !_views.empty()) {
955 DraggingView dv = _views.front();
956 dv.view->show_region_editor ();
963 /* reverse this here so that we have the correct logic to finalize
967 if (Config->get_edit_mode() == Lock) {
968 _x_constrained = !_x_constrained;
971 assert (!_views.empty ());
973 /* We might have hidden region views so that they weren't visible during the drag
974 (when they have been reparented). Now everything can be shown again, as region
975 views are back in their track parent groups.
977 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
978 i->view->get_canvas_group()->show ();
981 bool const changed_position = (_last_frame_position != _primary->region()->position());
982 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
983 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1003 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1007 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1009 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1014 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1015 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1016 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1017 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1019 rtav->set_height (original->current_height());
1023 ChanCount one_midi_port (DataType::MIDI, 1);
1024 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1025 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1026 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1028 rtav->set_height (original->current_height());
1033 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1039 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1041 RegionSelection new_views;
1042 PlaylistSet modified_playlists;
1043 RouteTimeAxisView* new_time_axis_view = 0;
1046 /* all changes were made during motion event handlers */
1048 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1052 _editor->commit_reversible_command ();
1056 /* insert the regions into their new playlists */
1057 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1059 RouteTimeAxisView* dest_rtv = 0;
1061 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1067 if (changed_position && !_x_constrained) {
1068 where = i->view->region()->position() - drag_delta;
1070 where = i->view->region()->position();
1073 if (i->time_axis_view < 0) {
1074 if (!new_time_axis_view) {
1075 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1077 dest_rtv = new_time_axis_view;
1079 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1082 if (dest_rtv != 0) {
1083 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1084 if (new_view != 0) {
1085 new_views.push_back (new_view);
1089 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1090 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1093 list<DraggingView>::const_iterator next = i;
1099 /* If we've created new regions either by copying or moving
1100 to a new track, we want to replace the old selection with the new ones
1103 if (new_views.size() > 0) {
1104 _editor->selection->set (new_views);
1107 /* write commands for the accumulated diffs for all our modified playlists */
1108 add_stateful_diff_commands_for_playlists (modified_playlists);
1110 _editor->commit_reversible_command ();
1114 RegionMoveDrag::finished_no_copy (
1115 bool const changed_position,
1116 bool const changed_tracks,
1117 framecnt_t const drag_delta
1120 RegionSelection new_views;
1121 PlaylistSet modified_playlists;
1122 PlaylistSet frozen_playlists;
1123 set<RouteTimeAxisView*> views_to_update;
1124 RouteTimeAxisView* new_time_axis_view = 0;
1127 /* all changes were made during motion event handlers */
1128 _editor->commit_reversible_command ();
1132 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1134 RegionView* rv = i->view;
1135 RouteTimeAxisView* dest_rtv = 0;
1137 if (rv->region()->locked() || rv->region()->video_locked()) {
1142 if (i->time_axis_view < 0) {
1143 if (!new_time_axis_view) {
1144 new_time_axis_view = create_destination_time_axis (rv->region(), i->initial_time_axis_view);
1146 dest_rtv = new_time_axis_view;
1148 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1153 double const dest_layer = i->layer;
1155 views_to_update.insert (dest_rtv);
1159 if (changed_position && !_x_constrained) {
1160 where = rv->region()->position() - drag_delta;
1162 where = rv->region()->position();
1165 if (changed_tracks) {
1167 /* insert into new playlist */
1169 RegionView* new_view = insert_region_into_playlist (
1170 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1173 if (new_view == 0) {
1178 new_views.push_back (new_view);
1180 /* remove from old playlist */
1182 /* the region that used to be in the old playlist is not
1183 moved to the new one - we use a copy of it. as a result,
1184 any existing editor for the region should no longer be
1187 rv->hide_region_editor();
1190 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1194 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1196 /* this movement may result in a crossfade being modified, or a layering change,
1197 so we need to get undo data from the playlist as well as the region.
1200 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1202 playlist->clear_changes ();
1205 rv->region()->clear_changes ();
1208 motion on the same track. plonk the previously reparented region
1209 back to its original canvas group (its streamview).
1210 No need to do anything for copies as they are fake regions which will be deleted.
1213 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1214 rv->get_canvas_group()->set_y_position (i->initial_y);
1217 /* just change the model */
1218 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1219 playlist->set_layer (rv->region(), dest_layer);
1222 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1224 r = frozen_playlists.insert (playlist);
1227 playlist->freeze ();
1230 rv->region()->set_position (where);
1232 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1235 if (changed_tracks) {
1237 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1238 was selected in all of them, then removing it from a playlist will have removed all
1239 trace of it from _views (i.e. there were N regions selected, we removed 1,
1240 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1241 corresponding regionview, and _views is now empty).
1243 This could have invalidated any and all iterators into _views.
1245 The heuristic we use here is: if the region selection is empty, break out of the loop
1246 here. if the region selection is not empty, then restart the loop because we know that
1247 we must have removed at least the region(view) we've just been working on as well as any
1248 that we processed on previous iterations.
1250 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1251 we can just iterate.
1255 if (_views.empty()) {
1266 /* If we've created new regions either by copying or moving
1267 to a new track, we want to replace the old selection with the new ones
1270 if (new_views.size() > 0) {
1271 _editor->selection->set (new_views);
1274 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1278 /* write commands for the accumulated diffs for all our modified playlists */
1279 add_stateful_diff_commands_for_playlists (modified_playlists);
1281 _editor->commit_reversible_command ();
1283 /* We have futzed with the layering of canvas items on our streamviews.
1284 If any region changed layer, this will have resulted in the stream
1285 views being asked to set up their region views, and all will be well.
1286 If not, we might now have badly-ordered region views. Ask the StreamViews
1287 involved to sort themselves out, just in case.
1290 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1291 (*i)->view()->playlist_layered ((*i)->track ());
1295 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1296 * @param region Region to remove.
1297 * @param playlist playlist To remove from.
1298 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1299 * that clear_changes () is only called once per playlist.
1302 RegionMoveDrag::remove_region_from_playlist (
1303 boost::shared_ptr<Region> region,
1304 boost::shared_ptr<Playlist> playlist,
1305 PlaylistSet& modified_playlists
1308 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1311 playlist->clear_changes ();
1314 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1318 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1319 * clearing the playlist's diff history first if necessary.
1320 * @param region Region to insert.
1321 * @param dest_rtv Destination RouteTimeAxisView.
1322 * @param dest_layer Destination layer.
1323 * @param where Destination position.
1324 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1325 * that clear_changes () is only called once per playlist.
1326 * @return New RegionView, or 0 if no insert was performed.
1329 RegionMoveDrag::insert_region_into_playlist (
1330 boost::shared_ptr<Region> region,
1331 RouteTimeAxisView* dest_rtv,
1334 PlaylistSet& modified_playlists
1337 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1338 if (!dest_playlist) {
1342 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1343 _new_region_view = 0;
1344 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1346 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1347 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1349 dest_playlist->clear_changes ();
1352 dest_playlist->add_region (region, where);
1354 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1355 dest_playlist->set_layer (region, dest_layer);
1360 assert (_new_region_view);
1362 return _new_region_view;
1366 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1368 _new_region_view = rv;
1372 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1374 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1375 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1377 _editor->session()->add_command (c);
1386 RegionMoveDrag::aborted (bool movement_occurred)
1390 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1391 list<DraggingView>::const_iterator next = i;
1400 RegionMotionDrag::aborted (movement_occurred);
1405 RegionMotionDrag::aborted (bool)
1407 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1409 StreamView* sview = (*i)->view();
1412 if (sview->layer_display() == Expanded) {
1413 sview->set_layer_display (Stacked);
1418 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1419 RegionView* rv = i->view;
1420 TimeAxisView* tv = &(rv->get_time_axis_view ());
1421 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1423 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1424 rv->get_canvas_group()->set_y_position (0);
1426 rv->move (-_total_x_delta, 0);
1427 rv->set_height (rtv->view()->child_height ());
1431 /** @param b true to brush, otherwise false.
1432 * @param c true to make copies of the regions being moved, otherwise false.
1434 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1435 : RegionMotionDrag (e, i, p, v, b),
1438 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1441 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1442 if (rtv && rtv->is_track()) {
1443 speed = rtv->track()->speed ();
1446 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1450 RegionMoveDrag::setup_pointer_frame_offset ()
1452 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1455 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1456 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1458 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1460 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1461 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1463 _primary = v->view()->create_region_view (r, false, false);
1465 _primary->get_canvas_group()->show ();
1466 _primary->set_position (pos, 0);
1467 _views.push_back (DraggingView (_primary, this, v));
1469 _last_frame_position = pos;
1471 _item = _primary->get_canvas_group ();
1475 RegionInsertDrag::finished (GdkEvent *, bool)
1477 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1479 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1480 _primary->get_canvas_group()->set_y_position (0);
1482 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1484 _editor->begin_reversible_command (Operations::insert_region);
1485 playlist->clear_changes ();
1486 playlist->add_region (_primary->region (), _last_frame_position);
1488 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1489 if (Config->get_edit_mode() == Ripple) {
1490 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1493 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1494 _editor->commit_reversible_command ();
1502 RegionInsertDrag::aborted (bool)
1509 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1510 : RegionMoveDrag (e, i, p, v, false, false)
1512 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1515 struct RegionSelectionByPosition {
1516 bool operator() (RegionView*a, RegionView* b) {
1517 return a->region()->position () < b->region()->position();
1522 RegionSpliceDrag::motion (GdkEvent* event, bool)
1524 /* Which trackview is this ? */
1526 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1527 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1529 /* The region motion is only processed if the pointer is over
1533 if (!tv || !tv->is_track()) {
1534 /* To make sure we hide the verbose canvas cursor when the mouse is
1535 not held over an audio track.
1537 _editor->verbose_cursor()->hide ();
1540 _editor->verbose_cursor()->show ();
1545 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1551 RegionSelection copy;
1552 _editor->selection->regions.by_position(copy);
1554 framepos_t const pf = adjusted_current_frame (event);
1556 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1558 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1564 boost::shared_ptr<Playlist> playlist;
1566 if ((playlist = atv->playlist()) == 0) {
1570 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1575 if (pf < (*i)->region()->last_frame() + 1) {
1579 if (pf > (*i)->region()->first_frame()) {
1585 playlist->shuffle ((*i)->region(), dir);
1590 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1592 RegionMoveDrag::finished (event, movement_occurred);
1596 RegionSpliceDrag::aborted (bool)
1606 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1609 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1611 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1612 RegionSelection to_ripple;
1613 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1614 if ((*i)->position() >= where) {
1615 to_ripple.push_back (rtv->view()->find_view(*i));
1619 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1620 if (!exclude.contains (*i)) {
1621 // the selection has already been added to _views
1623 if (drag_in_progress) {
1624 // do the same things that RegionMotionDrag::motion does when
1625 // first_move is true, for the region views that we're adding
1626 // to _views this time
1629 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1630 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1631 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1632 rvg->reparent (_editor->_drag_motion_group);
1634 // we only need to move in the y direction
1635 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1640 _views.push_back (DraggingView (*i, this, tav));
1646 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1649 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1650 // we added all the regions after the selection
1652 std::list<DraggingView>::iterator to_erase = i++;
1653 if (!_editor->selection->regions.contains (to_erase->view)) {
1654 // restore the non-selected regions to their original playlist & positions,
1655 // and then ripple them back by the length of the regions that were dragged away
1656 // do the same things as RegionMotionDrag::aborted
1658 RegionView *rv = to_erase->view;
1659 TimeAxisView* tv = &(rv->get_time_axis_view ());
1660 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1663 // plonk them back onto their own track
1664 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1665 rv->get_canvas_group()->set_y_position (0);
1669 // move the underlying region to match the view
1670 rv->region()->set_position (rv->region()->position() + amount);
1672 // restore the view to match the underlying region's original position
1673 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
1676 rv->set_height (rtv->view()->child_height ());
1677 _views.erase (to_erase);
1683 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const
1685 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) {
1687 return allow_moves_across_tracks;
1695 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1696 : RegionMoveDrag (e, i, p, v, false, false)
1698 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
1699 // compute length of selection
1700 RegionSelection selected_regions = _editor->selection->regions;
1701 selection_length = selected_regions.end_frame() - selected_regions.start();
1703 // we'll only allow dragging to another track in ripple mode if all the regions
1704 // being dragged start off on the same track
1705 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
1708 exclude = new RegionList;
1709 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
1710 exclude->push_back((*i)->region());
1713 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
1714 RegionSelection copy;
1715 selected_regions.by_position(copy); // get selected regions sorted by position into copy
1717 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
1718 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1720 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1721 // find ripple start point on each applicable playlist
1722 RegionView *first_selected_on_this_track = NULL;
1723 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1724 if ((*i)->region()->playlist() == (*pi)) {
1725 // region is on this playlist - it's the first, because they're sorted
1726 first_selected_on_this_track = *i;
1730 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
1731 add_all_after_to_views (
1732 &first_selected_on_this_track->get_time_axis_view(),
1733 first_selected_on_this_track->region()->position(),
1734 selected_regions, false);
1737 if (allow_moves_across_tracks) {
1738 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
1746 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
1748 /* Which trackview is this ? */
1750 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1751 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1753 /* The region motion is only processed if the pointer is over
1757 if (!tv || !tv->is_track()) {
1758 /* To make sure we hide the verbose canvas cursor when the mouse is
1759 not held over an audiotrack.
1761 _editor->verbose_cursor()->hide ();
1765 framepos_t where = adjusted_current_frame (event);
1766 assert (where >= 0);
1768 double delta = compute_x_delta (event, &after);
1770 framecnt_t amount = _editor->pixel_to_sample (delta);
1772 if (allow_moves_across_tracks) {
1773 // all the originally selected regions were on the same track
1775 framecnt_t adjust = 0;
1776 if (prev_tav && tv != prev_tav) {
1777 // dragged onto a different track
1778 // remove the unselected regions from _views, restore them to their original positions
1779 // and add the regions after the drop point on the new playlist to _views instead.
1780 // undo the effect of rippling the previous playlist, and include the effect of removing
1781 // the dragged region(s) from this track
1783 remove_unselected_from_views (prev_amount, false);
1784 // ripple previous playlist according to the regions that have been removed onto the new playlist
1785 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
1788 // move just the selected regions
1789 RegionMoveDrag::motion(event, first_move);
1791 // ensure that the ripple operation on the new playlist inserts selection_length time
1792 adjust = selection_length;
1793 // ripple the new current playlist
1794 tv->playlist()->ripple (where, amount+adjust, exclude);
1796 // add regions after point where drag entered this track to subsequent ripples
1797 add_all_after_to_views (tv, where, _editor->selection->regions, true);
1800 // motion on same track
1801 RegionMoveDrag::motion(event, first_move);
1805 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
1806 prev_position = where;
1808 // selection encompasses multiple tracks - just drag
1809 // cross-track drags are forbidden
1810 RegionMoveDrag::motion(event, first_move);
1813 if (!_x_constrained) {
1814 prev_amount += amount;
1817 _last_frame_position = after;
1821 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
1823 if (!movement_occurred) {
1827 if (was_double_click() && !_views.empty()) {
1828 DraggingView dv = _views.front();
1829 dv.view->show_region_editor ();
1836 _editor->begin_reversible_command(_("Ripple drag"));
1838 // remove the regions being rippled from the dragging view, updating them to
1839 // their new positions
1840 remove_unselected_from_views (prev_amount, true);
1842 if (allow_moves_across_tracks) {
1844 // if regions were dragged across tracks, we've rippled any later
1845 // regions on the track the regions were dragged off, so we need
1846 // to add the original track to the undo record
1847 orig_tav->playlist()->clear_changes();
1848 vector<Command*> cmds;
1849 orig_tav->playlist()->rdiff (cmds);
1850 _editor->session()->add_commands (cmds);
1852 if (prev_tav && prev_tav != orig_tav) {
1853 prev_tav->playlist()->clear_changes();
1854 vector<Command*> cmds;
1855 prev_tav->playlist()->rdiff (cmds);
1856 _editor->session()->add_commands (cmds);
1859 // selection spanned multiple tracks - all will need adding to undo record
1861 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
1862 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1864 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1865 (*pi)->clear_changes();
1866 vector<Command*> cmds;
1867 (*pi)->rdiff (cmds);
1868 _editor->session()->add_commands (cmds);
1872 // other modified playlists are added to undo by RegionMoveDrag::finished()
1873 RegionMoveDrag::finished (event, movement_occurred);
1874 _editor->commit_reversible_command();
1878 RegionRippleDrag::aborted (bool movement_occurred)
1880 RegionMoveDrag::aborted (movement_occurred);
1885 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1887 _view (dynamic_cast<MidiTimeAxisView*> (v))
1889 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1895 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1898 _region = add_midi_region (_view);
1899 _view->playlist()->freeze ();
1902 framepos_t const f = adjusted_current_frame (event);
1903 if (f < grab_frame()) {
1904 _region->set_position (f);
1907 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1908 so that if this region is duplicated, its duplicate starts on
1909 a snap point rather than 1 frame after a snap point. Otherwise things get
1910 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1911 place snapped notes at the start of the region.
1914 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1915 _region->set_length (len < 1 ? 1 : len);
1921 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1923 if (!movement_occurred) {
1924 add_midi_region (_view);
1926 _view->playlist()->thaw ();
1931 RegionCreateDrag::aborted (bool)
1934 _view->playlist()->thaw ();
1940 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1944 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1948 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1950 Gdk::Cursor* cursor;
1951 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1953 float x_fraction = cnote->mouse_x_fraction ();
1955 if (x_fraction > 0.0 && x_fraction < 0.25) {
1956 cursor = _editor->cursors()->left_side_trim;
1959 cursor = _editor->cursors()->right_side_trim;
1963 Drag::start_grab (event, cursor);
1965 region = &cnote->region_view();
1969 if (event->motion.state & Keyboard::PrimaryModifier) {
1975 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1977 if (ms.size() > 1) {
1978 /* has to be relative, may make no sense otherwise */
1982 /* select this note; if it is already selected, preserve the existing selection,
1983 otherwise make this note the only one selected.
1985 region->note_selected (cnote, cnote->selected ());
1987 _editor->begin_reversible_command (_("resize notes"));
1989 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1990 MidiRegionSelection::iterator next;
1993 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
1995 mrv->begin_resizing (at_front);
2002 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
2004 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2005 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2006 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2008 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2010 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2016 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
2018 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2019 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2020 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2022 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2024 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2028 _editor->commit_reversible_command ();
2032 NoteResizeDrag::aborted (bool)
2034 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2035 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2036 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2038 mrv->abort_resizing ();
2043 AVDraggingView::AVDraggingView (RegionView* v)
2046 initial_position = v->region()->position ();
2049 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2052 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2055 TrackViewList empty;
2057 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2058 std::list<RegionView*> views = rs.by_layer();
2060 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2061 RegionView* rv = (*i);
2062 if (!rv->region()->video_locked()) {
2065 _views.push_back (AVDraggingView (rv));
2070 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2072 Drag::start_grab (event);
2073 if (_editor->session() == 0) {
2077 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2078 _max_backwards_drag = (
2079 ARDOUR_UI::instance()->video_timeline->get_duration()
2080 + ARDOUR_UI::instance()->video_timeline->get_offset()
2081 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2084 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2085 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2086 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2089 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2092 Timecode::Time timecode;
2093 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2094 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);
2095 show_verbose_cursor_text (buf);
2099 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2101 if (_editor->session() == 0) {
2104 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2108 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2109 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2111 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2112 dt = - _max_backwards_drag;
2115 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2116 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2118 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2119 RegionView* rv = i->view;
2120 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2123 rv->region()->clear_changes ();
2124 rv->region()->suspend_property_changes();
2126 rv->region()->set_position(i->initial_position + dt);
2127 rv->region_changed(ARDOUR::Properties::position);
2130 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2131 Timecode::Time timecode;
2132 Timecode::Time timediff;
2134 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2135 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2136 snprintf (buf, sizeof (buf),
2137 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2138 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2139 , _("Video Start:"),
2140 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2142 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2144 show_verbose_cursor_text (buf);
2148 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2150 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2154 if (!movement_occurred || ! _editor->session()) {
2158 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2160 _editor->begin_reversible_command (_("Move Video"));
2162 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2163 ARDOUR_UI::instance()->video_timeline->save_undo();
2164 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2165 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2167 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2168 i->view->drag_end();
2169 i->view->region()->resume_property_changes ();
2171 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2174 _editor->session()->maybe_update_session_range(
2175 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2176 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2180 _editor->commit_reversible_command ();
2184 VideoTimeLineDrag::aborted (bool)
2186 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2189 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2190 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2192 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2193 i->view->region()->resume_property_changes ();
2194 i->view->region()->set_position(i->initial_position);
2198 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2199 : RegionDrag (e, i, p, v)
2200 , _preserve_fade_anchor (preserve_fade_anchor)
2201 , _jump_position_when_done (false)
2203 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2207 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2210 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2211 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2213 if (tv && tv->is_track()) {
2214 speed = tv->track()->speed();
2217 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2218 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2219 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2221 framepos_t const pf = adjusted_current_frame (event);
2223 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2224 /* Move the contents of the region around without changing the region bounds */
2225 _operation = ContentsTrim;
2226 Drag::start_grab (event, _editor->cursors()->trimmer);
2228 /* These will get overridden for a point trim.*/
2229 if (pf < (region_start + region_length/2)) {
2230 /* closer to front */
2231 _operation = StartTrim;
2233 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2234 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2236 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2240 _operation = EndTrim;
2241 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2242 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2244 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2249 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2250 _jump_position_when_done = true;
2253 switch (_operation) {
2255 show_verbose_cursor_time (region_start);
2256 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2257 i->view->trim_front_starting ();
2261 show_verbose_cursor_time (region_end);
2264 show_verbose_cursor_time (pf);
2268 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2269 i->view->region()->suspend_property_changes ();
2274 TrimDrag::motion (GdkEvent* event, bool first_move)
2276 RegionView* rv = _primary;
2279 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2280 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2281 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2282 frameoffset_t frame_delta = 0;
2284 if (tv && tv->is_track()) {
2285 speed = tv->track()->speed();
2288 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2294 switch (_operation) {
2296 trim_type = "Region start trim";
2299 trim_type = "Region end trim";
2302 trim_type = "Region content trim";
2309 _editor->begin_reversible_command (trim_type);
2311 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2312 RegionView* rv = i->view;
2313 rv->enable_display (false);
2314 rv->region()->playlist()->clear_owned_changes ();
2316 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2319 arv->temporarily_hide_envelope ();
2323 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2324 insert_result = _editor->motion_frozen_playlists.insert (pl);
2326 if (insert_result.second) {
2332 bool non_overlap_trim = false;
2334 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2335 non_overlap_trim = true;
2338 /* contstrain trim to fade length */
2339 if (_preserve_fade_anchor) {
2340 switch (_operation) {
2342 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2343 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2345 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2346 if (ar->locked()) continue;
2347 framecnt_t len = ar->fade_in()->back()->when;
2348 if (len < dt) dt = min(dt, len);
2352 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2353 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2355 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2356 if (ar->locked()) continue;
2357 framecnt_t len = ar->fade_out()->back()->when;
2358 if (len < -dt) dt = max(dt, -len);
2367 switch (_operation) {
2369 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2370 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2371 if (changed && _preserve_fade_anchor) {
2372 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2374 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2375 framecnt_t len = ar->fade_in()->back()->when;
2376 framecnt_t diff = ar->first_frame() - i->initial_position;
2377 framepos_t new_length = len - diff;
2378 i->anchored_fade_length = min (ar->length(), new_length);
2379 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2380 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2387 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2388 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2389 if (changed && _preserve_fade_anchor) {
2390 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2392 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2393 framecnt_t len = ar->fade_out()->back()->when;
2394 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2395 framepos_t new_length = len + diff;
2396 i->anchored_fade_length = min (ar->length(), new_length);
2397 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2398 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2406 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2408 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2409 i->view->move_contents (frame_delta);
2415 switch (_operation) {
2417 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2420 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2423 // show_verbose_cursor_time (frame_delta);
2430 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2432 if (movement_occurred) {
2433 motion (event, false);
2435 if (_operation == StartTrim) {
2436 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2438 /* This must happen before the region's StatefulDiffCommand is created, as it may
2439 `correct' (ahem) the region's _start from being negative to being zero. It
2440 needs to be zero in the undo record.
2442 i->view->trim_front_ending ();
2444 if (_preserve_fade_anchor) {
2445 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2447 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2448 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2449 ar->set_fade_in_length(i->anchored_fade_length);
2450 ar->set_fade_in_active(true);
2453 if (_jump_position_when_done) {
2454 i->view->region()->set_position (i->initial_position);
2457 } else if (_operation == EndTrim) {
2458 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2459 if (_preserve_fade_anchor) {
2460 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2462 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2463 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2464 ar->set_fade_out_length(i->anchored_fade_length);
2465 ar->set_fade_out_active(true);
2468 if (_jump_position_when_done) {
2469 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2474 if (!_views.empty()) {
2475 if (_operation == StartTrim) {
2476 _editor->maybe_locate_with_edit_preroll(
2477 _views.begin()->view->region()->position());
2479 if (_operation == EndTrim) {
2480 _editor->maybe_locate_with_edit_preroll(
2481 _views.begin()->view->region()->position() +
2482 _views.begin()->view->region()->length());
2486 if (!_editor->selection->selected (_primary)) {
2487 _primary->thaw_after_trim ();
2490 set<boost::shared_ptr<Playlist> > diffed_playlists;
2492 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2493 i->view->thaw_after_trim ();
2494 i->view->enable_display (true);
2496 /* Trimming one region may affect others on the playlist, so we need
2497 to get undo Commands from the whole playlist rather than just the
2498 region. Use diffed_playlists to make sure we don't diff a given
2499 playlist more than once.
2501 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2502 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2503 vector<Command*> cmds;
2505 _editor->session()->add_commands (cmds);
2506 diffed_playlists.insert (p);
2511 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2515 _editor->motion_frozen_playlists.clear ();
2516 _editor->commit_reversible_command();
2519 /* no mouse movement */
2520 _editor->point_trim (event, adjusted_current_frame (event));
2523 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2524 if (_operation == StartTrim) {
2525 i->view->trim_front_ending ();
2528 i->view->region()->resume_property_changes ();
2533 TrimDrag::aborted (bool movement_occurred)
2535 /* Our motion method is changing model state, so use the Undo system
2536 to cancel. Perhaps not ideal, as this will leave an Undo point
2537 behind which may be slightly odd from the user's point of view.
2542 if (movement_occurred) {
2546 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2547 i->view->region()->resume_property_changes ();
2552 TrimDrag::setup_pointer_frame_offset ()
2554 list<DraggingView>::iterator i = _views.begin ();
2555 while (i != _views.end() && i->view != _primary) {
2559 if (i == _views.end()) {
2563 switch (_operation) {
2565 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2568 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2575 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2579 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2580 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2585 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2587 Drag::start_grab (event, cursor);
2588 show_verbose_cursor_time (adjusted_current_frame(event));
2592 MeterMarkerDrag::setup_pointer_frame_offset ()
2594 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2598 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2600 if (!_marker->meter().movable()) {
2606 // create a dummy marker for visual representation of moving the
2607 // section, because whether its a copy or not, we're going to
2608 // leave or lose the original marker (leave if its a copy; lose if its
2609 // not, because we'll remove it from the map).
2611 MeterSection section (_marker->meter());
2613 if (!section.movable()) {
2618 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2620 _marker = new MeterMarker (
2622 *_editor->meter_group,
2623 ARDOUR_UI::config()->color ("meter marker"),
2625 *new MeterSection (_marker->meter())
2628 /* use the new marker for the grab */
2629 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2632 TempoMap& map (_editor->session()->tempo_map());
2633 /* get current state */
2634 before_state = &map.get_state();
2635 /* remove the section while we drag it */
2636 map.remove_meter (section, true);
2640 framepos_t const pf = adjusted_current_frame (event);
2642 _marker->set_position (pf);
2643 show_verbose_cursor_time (pf);
2647 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2649 if (!movement_occurred) {
2650 if (was_double_click()) {
2651 _editor->edit_meter_marker (*_marker);
2656 if (!_marker->meter().movable()) {
2660 motion (event, false);
2662 Timecode::BBT_Time when;
2664 TempoMap& map (_editor->session()->tempo_map());
2665 map.bbt_time (last_pointer_frame(), when);
2667 if (_copy == true) {
2668 _editor->begin_reversible_command (_("copy meter mark"));
2669 XMLNode &before = map.get_state();
2670 map.add_meter (_marker->meter(), when);
2671 XMLNode &after = map.get_state();
2672 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2673 _editor->commit_reversible_command ();
2676 _editor->begin_reversible_command (_("move meter mark"));
2678 /* we removed it before, so add it back now */
2680 map.add_meter (_marker->meter(), when);
2681 XMLNode &after = map.get_state();
2682 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2683 _editor->commit_reversible_command ();
2686 // delete the dummy marker we used for visual representation while moving.
2687 // a new visual marker will show up automatically.
2692 MeterMarkerDrag::aborted (bool moved)
2694 _marker->set_position (_marker->meter().frame ());
2697 TempoMap& map (_editor->session()->tempo_map());
2698 /* we removed it before, so add it back now */
2699 map.add_meter (_marker->meter(), _marker->meter().frame());
2700 // delete the dummy marker we used for visual representation while moving.
2701 // a new visual marker will show up automatically.
2706 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2710 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2712 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2717 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2719 Drag::start_grab (event, cursor);
2720 show_verbose_cursor_time (adjusted_current_frame (event));
2724 TempoMarkerDrag::setup_pointer_frame_offset ()
2726 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2730 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2732 if (!_marker->tempo().movable()) {
2738 // create a dummy marker for visual representation of moving the
2739 // section, because whether its a copy or not, we're going to
2740 // leave or lose the original marker (leave if its a copy; lose if its
2741 // not, because we'll remove it from the map).
2743 // create a dummy marker for visual representation of moving the copy.
2744 // The actual copying is not done before we reach the finish callback.
2747 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2749 TempoSection section (_marker->tempo());
2751 _marker = new TempoMarker (
2753 *_editor->tempo_group,
2754 ARDOUR_UI::config()->color ("tempo marker"),
2756 *new TempoSection (_marker->tempo())
2759 /* use the new marker for the grab */
2760 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2763 TempoMap& map (_editor->session()->tempo_map());
2764 /* get current state */
2765 before_state = &map.get_state();
2766 /* remove the section while we drag it */
2767 map.remove_tempo (section, true);
2771 framepos_t const pf = adjusted_current_frame (event);
2772 _marker->set_position (pf);
2773 show_verbose_cursor_time (pf);
2777 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2779 if (!movement_occurred) {
2780 if (was_double_click()) {
2781 _editor->edit_tempo_marker (*_marker);
2786 if (!_marker->tempo().movable()) {
2790 motion (event, false);
2792 TempoMap& map (_editor->session()->tempo_map());
2793 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
2794 Timecode::BBT_Time when;
2796 map.bbt_time (beat_time, when);
2798 if (_copy == true) {
2799 _editor->begin_reversible_command (_("copy tempo mark"));
2800 XMLNode &before = map.get_state();
2801 map.add_tempo (_marker->tempo(), when);
2802 XMLNode &after = map.get_state();
2803 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2804 _editor->commit_reversible_command ();
2807 _editor->begin_reversible_command (_("move tempo mark"));
2808 /* we removed it before, so add it back now */
2809 map.add_tempo (_marker->tempo(), when);
2810 XMLNode &after = map.get_state();
2811 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2812 _editor->commit_reversible_command ();
2815 // delete the dummy marker we used for visual representation while moving.
2816 // a new visual marker will show up automatically.
2821 TempoMarkerDrag::aborted (bool moved)
2823 _marker->set_position (_marker->tempo().frame());
2825 TempoMap& map (_editor->session()->tempo_map());
2826 /* we removed it before, so add it back now */
2827 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2828 // delete the dummy marker we used for visual representation while moving.
2829 // a new visual marker will show up automatically.
2834 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2835 : Drag (e, &c.track_canvas_item(), false)
2839 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2842 /** Do all the things we do when dragging the playhead to make it look as though
2843 * we have located, without actually doing the locate (because that would cause
2844 * the diskstream buffers to be refilled, which is too slow).
2847 CursorDrag::fake_locate (framepos_t t)
2849 _editor->playhead_cursor->set_position (t);
2851 Session* s = _editor->session ();
2852 if (s->timecode_transmission_suspended ()) {
2853 framepos_t const f = _editor->playhead_cursor->current_frame ();
2854 /* This is asynchronous so it will be sent "now"
2856 s->send_mmc_locate (f);
2857 /* These are synchronous and will be sent during the next
2860 s->queue_full_time_code ();
2861 s->queue_song_position_pointer ();
2864 show_verbose_cursor_time (t);
2865 _editor->UpdateAllTransportClocks (t);
2869 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2871 Drag::start_grab (event, c);
2873 _grab_zoom = _editor->samples_per_pixel;
2875 framepos_t where = _editor->canvas_event_sample (event);
2877 _editor->snap_to_with_modifier (where, event);
2879 _editor->_dragging_playhead = true;
2881 Session* s = _editor->session ();
2883 /* grab the track canvas item as well */
2885 _cursor.track_canvas_item().grab();
2888 if (_was_rolling && _stop) {
2892 if (s->is_auditioning()) {
2893 s->cancel_audition ();
2897 if (AudioEngine::instance()->connected()) {
2899 /* do this only if we're the engine is connected
2900 * because otherwise this request will never be
2901 * serviced and we'll busy wait forever. likewise,
2902 * notice if we are disconnected while waiting for the
2903 * request to be serviced.
2906 s->request_suspend_timecode_transmission ();
2907 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2908 /* twiddle our thumbs */
2913 fake_locate (where);
2917 CursorDrag::motion (GdkEvent* event, bool)
2919 framepos_t const adjusted_frame = adjusted_current_frame (event);
2920 if (adjusted_frame != last_pointer_frame()) {
2921 fake_locate (adjusted_frame);
2926 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2928 _editor->_dragging_playhead = false;
2930 _cursor.track_canvas_item().ungrab();
2932 if (!movement_occurred && _stop) {
2936 motion (event, false);
2938 Session* s = _editor->session ();
2940 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2941 _editor->_pending_locate_request = true;
2942 s->request_resume_timecode_transmission ();
2947 CursorDrag::aborted (bool)
2949 _cursor.track_canvas_item().ungrab();
2951 if (_editor->_dragging_playhead) {
2952 _editor->session()->request_resume_timecode_transmission ();
2953 _editor->_dragging_playhead = false;
2956 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2959 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2960 : RegionDrag (e, i, p, v)
2962 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2966 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2968 Drag::start_grab (event, cursor);
2970 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2971 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2973 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2977 FadeInDrag::setup_pointer_frame_offset ()
2979 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2980 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2981 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2985 FadeInDrag::motion (GdkEvent* event, bool)
2987 framecnt_t fade_length;
2989 framepos_t const pos = adjusted_current_frame (event);
2991 boost::shared_ptr<Region> region = _primary->region ();
2993 if (pos < (region->position() + 64)) {
2994 fade_length = 64; // this should be a minimum defined somewhere
2995 } else if (pos > region->last_frame()) {
2996 fade_length = region->length();
2998 fade_length = pos - region->position();
3001 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3003 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3009 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3012 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3016 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3018 if (!movement_occurred) {
3022 framecnt_t fade_length;
3024 framepos_t const pos = adjusted_current_frame (event);
3026 boost::shared_ptr<Region> region = _primary->region ();
3028 if (pos < (region->position() + 64)) {
3029 fade_length = 64; // this should be a minimum defined somewhere
3030 } else if (pos > region->last_frame()) {
3031 fade_length = region->length();
3033 fade_length = pos - region->position();
3036 _editor->begin_reversible_command (_("change fade in length"));
3038 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3040 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3046 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3047 XMLNode &before = alist->get_state();
3049 tmp->audio_region()->set_fade_in_length (fade_length);
3050 tmp->audio_region()->set_fade_in_active (true);
3052 XMLNode &after = alist->get_state();
3053 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3056 _editor->commit_reversible_command ();
3060 FadeInDrag::aborted (bool)
3062 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3063 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3069 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3073 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3074 : RegionDrag (e, i, p, v)
3076 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3080 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3082 Drag::start_grab (event, cursor);
3084 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3085 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3087 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3091 FadeOutDrag::setup_pointer_frame_offset ()
3093 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3094 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3095 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3099 FadeOutDrag::motion (GdkEvent* event, bool)
3101 framecnt_t fade_length;
3103 framepos_t const pos = adjusted_current_frame (event);
3105 boost::shared_ptr<Region> region = _primary->region ();
3107 if (pos > (region->last_frame() - 64)) {
3108 fade_length = 64; // this should really be a minimum fade defined somewhere
3110 else if (pos < region->position()) {
3111 fade_length = region->length();
3114 fade_length = region->last_frame() - pos;
3117 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3119 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3125 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3128 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3132 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3134 if (!movement_occurred) {
3138 framecnt_t fade_length;
3140 framepos_t const pos = adjusted_current_frame (event);
3142 boost::shared_ptr<Region> region = _primary->region ();
3144 if (pos > (region->last_frame() - 64)) {
3145 fade_length = 64; // this should really be a minimum fade defined somewhere
3147 else if (pos < region->position()) {
3148 fade_length = region->length();
3151 fade_length = region->last_frame() - pos;
3154 _editor->begin_reversible_command (_("change fade out length"));
3156 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3158 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3164 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3165 XMLNode &before = alist->get_state();
3167 tmp->audio_region()->set_fade_out_length (fade_length);
3168 tmp->audio_region()->set_fade_out_active (true);
3170 XMLNode &after = alist->get_state();
3171 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3174 _editor->commit_reversible_command ();
3178 FadeOutDrag::aborted (bool)
3180 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3181 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3187 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3191 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3194 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3196 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3199 _points.push_back (ArdourCanvas::Duple (0, 0));
3200 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3203 MarkerDrag::~MarkerDrag ()
3205 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3210 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3212 location = new Location (*l);
3213 markers.push_back (m);
3218 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3220 Drag::start_grab (event, cursor);
3224 Location *location = _editor->find_location_from_marker (_marker, is_start);
3225 _editor->_dragging_edit_point = true;
3227 update_item (location);
3229 // _drag_line->show();
3230 // _line->raise_to_top();
3233 show_verbose_cursor_time (location->start());
3235 show_verbose_cursor_time (location->end());
3238 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3241 case Selection::Toggle:
3242 /* we toggle on the button release */
3244 case Selection::Set:
3245 if (!_editor->selection->selected (_marker)) {
3246 _editor->selection->set (_marker);
3249 case Selection::Extend:
3251 Locations::LocationList ll;
3252 list<Marker*> to_add;
3254 _editor->selection->markers.range (s, e);
3255 s = min (_marker->position(), s);
3256 e = max (_marker->position(), e);
3259 if (e < max_framepos) {
3262 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3263 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3264 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3267 to_add.push_back (lm->start);
3270 to_add.push_back (lm->end);
3274 if (!to_add.empty()) {
3275 _editor->selection->add (to_add);
3279 case Selection::Add:
3280 _editor->selection->add (_marker);
3284 /* Set up copies for us to manipulate during the drag
3287 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3289 Location* l = _editor->find_location_from_marker (*i, is_start);
3296 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3298 /* range: check that the other end of the range isn't
3301 CopiedLocationInfo::iterator x;
3302 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3303 if (*(*x).location == *l) {
3307 if (x == _copied_locations.end()) {
3308 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3310 (*x).markers.push_back (*i);
3311 (*x).move_both = true;
3319 MarkerDrag::setup_pointer_frame_offset ()
3322 Location *location = _editor->find_location_from_marker (_marker, is_start);
3323 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3327 MarkerDrag::motion (GdkEvent* event, bool)
3329 framecnt_t f_delta = 0;
3331 bool move_both = false;
3332 Location *real_location;
3333 Location *copy_location = 0;
3335 framepos_t const newframe = adjusted_current_frame (event);
3336 framepos_t next = newframe;
3338 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3342 CopiedLocationInfo::iterator x;
3344 /* find the marker we're dragging, and compute the delta */
3346 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3348 copy_location = (*x).location;
3350 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3352 /* this marker is represented by this
3353 * CopiedLocationMarkerInfo
3356 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3361 if (real_location->is_mark()) {
3362 f_delta = newframe - copy_location->start();
3366 switch (_marker->type()) {
3367 case Marker::SessionStart:
3368 case Marker::RangeStart:
3369 case Marker::LoopStart:
3370 case Marker::PunchIn:
3371 f_delta = newframe - copy_location->start();
3374 case Marker::SessionEnd:
3375 case Marker::RangeEnd:
3376 case Marker::LoopEnd:
3377 case Marker::PunchOut:
3378 f_delta = newframe - copy_location->end();
3381 /* what kind of marker is this ? */
3390 if (x == _copied_locations.end()) {
3391 /* hmm, impossible - we didn't find the dragged marker */
3395 /* now move them all */
3397 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3399 copy_location = x->location;
3401 /* call this to find out if its the start or end */
3403 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3407 if (real_location->locked()) {
3411 if (copy_location->is_mark()) {
3415 copy_location->set_start (copy_location->start() + f_delta);
3419 framepos_t new_start = copy_location->start() + f_delta;
3420 framepos_t new_end = copy_location->end() + f_delta;
3422 if (is_start) { // start-of-range marker
3424 if (move_both || (*x).move_both) {
3425 copy_location->set_start (new_start);
3426 copy_location->set_end (new_end);
3427 } else if (new_start < copy_location->end()) {
3428 copy_location->set_start (new_start);
3429 } else if (newframe > 0) {
3430 _editor->snap_to (next, RoundUpAlways, true);
3431 copy_location->set_end (next);
3432 copy_location->set_start (newframe);
3435 } else { // end marker
3437 if (move_both || (*x).move_both) {
3438 copy_location->set_end (new_end);
3439 copy_location->set_start (new_start);
3440 } else if (new_end > copy_location->start()) {
3441 copy_location->set_end (new_end);
3442 } else if (newframe > 0) {
3443 _editor->snap_to (next, RoundDownAlways, true);
3444 copy_location->set_start (next);
3445 copy_location->set_end (newframe);
3450 update_item (copy_location);
3452 /* now lookup the actual GUI items used to display this
3453 * location and move them to wherever the copy of the location
3454 * is now. This means that the logic in ARDOUR::Location is
3455 * still enforced, even though we are not (yet) modifying
3456 * the real Location itself.
3459 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3462 lm->set_position (copy_location->start(), copy_location->end());
3467 assert (!_copied_locations.empty());
3469 show_verbose_cursor_time (newframe);
3473 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3475 if (!movement_occurred) {
3477 if (was_double_click()) {
3478 _editor->rename_marker (_marker);
3482 /* just a click, do nothing but finish
3483 off the selection process
3486 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3489 case Selection::Set:
3490 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3491 _editor->selection->set (_marker);
3495 case Selection::Toggle:
3496 /* we toggle on the button release, click only */
3497 _editor->selection->toggle (_marker);
3500 case Selection::Extend:
3501 case Selection::Add:
3508 _editor->_dragging_edit_point = false;
3510 _editor->begin_reversible_command ( _("move marker") );
3511 XMLNode &before = _editor->session()->locations()->get_state();
3513 MarkerSelection::iterator i;
3514 CopiedLocationInfo::iterator x;
3517 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3518 x != _copied_locations.end() && i != _editor->selection->markers.end();
3521 Location * location = _editor->find_location_from_marker (*i, is_start);
3525 if (location->locked()) {
3529 if (location->is_mark()) {
3530 location->set_start (((*x).location)->start());
3532 location->set (((*x).location)->start(), ((*x).location)->end());
3537 XMLNode &after = _editor->session()->locations()->get_state();
3538 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3539 _editor->commit_reversible_command ();
3543 MarkerDrag::aborted (bool movement_occured)
3545 if (!movement_occured) {
3549 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3551 /* move all markers to their original location */
3553 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3554 (*m)->set_position ((*m)->location()->start(), (*m)->location()->end());
3560 MarkerDrag::update_item (Location*)
3565 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3567 _cumulative_x_drag (0),
3568 _cumulative_y_drag (0)
3570 if (_zero_gain_fraction < 0.0) {
3571 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3574 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3576 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3582 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3584 Drag::start_grab (event, _editor->cursors()->fader);
3586 // start the grab at the center of the control point so
3587 // the point doesn't 'jump' to the mouse after the first drag
3588 _fixed_grab_x = _point->get_x();
3589 _fixed_grab_y = _point->get_y();
3591 float const fraction = 1 - (_point->get_y() / _point->line().height());
3593 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3595 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3597 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3599 if (!_point->can_slide ()) {
3600 _x_constrained = true;
3605 ControlPointDrag::motion (GdkEvent* event, bool)
3607 double dx = _drags->current_pointer_x() - last_pointer_x();
3608 double dy = current_pointer_y() - last_pointer_y();
3610 if (event->button.state & Keyboard::SecondaryModifier) {
3615 /* coordinate in pixels relative to the start of the region (for region-based automation)
3616 or track (for track-based automation) */
3617 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3618 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3620 // calculate zero crossing point. back off by .01 to stay on the
3621 // positive side of zero
3622 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3624 // make sure we hit zero when passing through
3625 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3629 if (_x_constrained) {
3632 if (_y_constrained) {
3636 _cumulative_x_drag = cx - _fixed_grab_x;
3637 _cumulative_y_drag = cy - _fixed_grab_y;
3641 cy = min ((double) _point->line().height(), cy);
3643 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3645 if (!_x_constrained) {
3646 _editor->snap_to_with_modifier (cx_frames, event);
3649 cx_frames = min (cx_frames, _point->line().maximum_time());
3651 float const fraction = 1.0 - (cy / _point->line().height());
3653 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3655 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3659 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3661 if (!movement_occurred) {
3665 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3666 _editor->reset_point_selection ();
3670 motion (event, false);
3673 _point->line().end_drag (_pushing, _final_index);
3674 _editor->commit_reversible_command ();
3678 ControlPointDrag::aborted (bool)
3680 _point->line().reset ();
3684 ControlPointDrag::active (Editing::MouseMode m)
3686 if (m == Editing::MouseDraw) {
3687 /* always active in mouse draw */
3691 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3692 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3695 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3698 _cumulative_y_drag (0)
3700 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3704 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3706 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3709 _item = &_line->grab_item ();
3711 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3712 origin, and ditto for y.
3715 double cx = event->button.x;
3716 double cy = event->button.y;
3718 _line->parent_group().canvas_to_item (cx, cy);
3720 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3725 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3726 /* no adjacent points */
3730 Drag::start_grab (event, _editor->cursors()->fader);
3732 /* store grab start in parent frame */
3737 double fraction = 1.0 - (cy / _line->height());
3739 _line->start_drag_line (before, after, fraction);
3741 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3745 LineDrag::motion (GdkEvent* event, bool)
3747 double dy = current_pointer_y() - last_pointer_y();
3749 if (event->button.state & Keyboard::SecondaryModifier) {
3753 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3755 _cumulative_y_drag = cy - _fixed_grab_y;
3758 cy = min ((double) _line->height(), cy);
3760 double const fraction = 1.0 - (cy / _line->height());
3763 /* we are ignoring x position for this drag, so we can just pass in anything */
3764 _line->drag_motion (0, fraction, true, false, ignored);
3766 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3770 LineDrag::finished (GdkEvent* event, bool movement_occured)
3772 if (movement_occured) {
3773 motion (event, false);
3774 _line->end_drag (false, 0);
3776 /* add a new control point on the line */
3778 AutomationTimeAxisView* atv;
3780 _line->end_drag (false, 0);
3782 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3783 framepos_t where = _editor->window_event_sample (event, 0, 0);
3784 atv->add_automation_event (event, where, event->button.y, false);
3788 _editor->commit_reversible_command ();
3792 LineDrag::aborted (bool)
3797 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3800 _cumulative_x_drag (0)
3802 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3806 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3808 Drag::start_grab (event);
3810 _line = reinterpret_cast<Line*> (_item);
3813 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3815 double cx = event->button.x;
3816 double cy = event->button.y;
3818 _item->parent()->canvas_to_item (cx, cy);
3820 /* store grab start in parent frame */
3821 _region_view_grab_x = cx;
3823 _before = *(float*) _item->get_data ("position");
3825 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3827 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3831 FeatureLineDrag::motion (GdkEvent*, bool)
3833 double dx = _drags->current_pointer_x() - last_pointer_x();
3835 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3837 _cumulative_x_drag += dx;
3839 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3848 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3850 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3852 float *pos = new float;
3855 _line->set_data ("position", pos);
3861 FeatureLineDrag::finished (GdkEvent*, bool)
3863 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3864 _arv->update_transient(_before, _before);
3868 FeatureLineDrag::aborted (bool)
3873 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3875 , _vertical_only (false)
3877 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3881 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3883 Drag::start_grab (event);
3884 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
3888 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3895 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
3897 framepos_t grab = grab_frame ();
3898 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
3899 _editor->snap_to_with_modifier (grab, event);
3901 grab = raw_grab_frame ();
3904 /* base start and end on initial click position */
3914 if (current_pointer_y() < grab_y()) {
3915 y1 = current_pointer_y();
3918 y2 = current_pointer_y();
3922 if (start != end || y1 != y2) {
3924 double x1 = _editor->sample_to_pixel (start);
3925 double x2 = _editor->sample_to_pixel (end);
3926 const double min_dimension = 2.0;
3928 if (_vertical_only) {
3929 /* fixed 10 pixel width */
3933 x2 = min (x1 - min_dimension, x2);
3935 x2 = max (x1 + min_dimension, x2);
3940 y2 = min (y1 - min_dimension, y2);
3942 y2 = max (y1 + min_dimension, y2);
3945 /* translate rect into item space and set */
3947 ArdourCanvas::Rect r (x1, y1, x2, y2);
3949 /* this drag is a _trackview_only == true drag, so the y1 and
3950 * y2 (computed using current_pointer_y() and grab_y()) will be
3951 * relative to the top of the trackview group). The
3952 * rubberband rect has the same parent/scroll offset as the
3953 * the trackview group, so we can use the "r" rect directly
3954 * to set the shape of the rubberband.
3957 _editor->rubberband_rect->set (r);
3958 _editor->rubberband_rect->show();
3959 _editor->rubberband_rect->raise_to_top();
3961 show_verbose_cursor_time (pf);
3963 do_select_things (event, true);
3968 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3972 framepos_t grab = grab_frame ();
3973 framepos_t lpf = last_pointer_frame ();
3975 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
3976 grab = raw_grab_frame ();
3977 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
3991 if (current_pointer_y() < grab_y()) {
3992 y1 = current_pointer_y();
3995 y2 = current_pointer_y();
3999 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4003 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4005 if (movement_occurred) {
4007 motion (event, false);
4008 do_select_things (event, false);
4014 bool do_deselect = true;
4015 MidiTimeAxisView* mtv;
4017 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4019 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4020 /* nothing selected */
4021 add_midi_region (mtv);
4022 do_deselect = false;
4026 /* do not deselect if Primary or Tertiary (toggle-select or
4027 * extend-select are pressed.
4030 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4031 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4038 _editor->rubberband_rect->hide();
4042 RubberbandSelectDrag::aborted (bool)
4044 _editor->rubberband_rect->hide ();
4047 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4048 : RegionDrag (e, i, p, v)
4050 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4054 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4056 Drag::start_grab (event, cursor);
4058 show_verbose_cursor_time (adjusted_current_frame (event));
4062 TimeFXDrag::motion (GdkEvent* event, bool)
4064 RegionView* rv = _primary;
4065 StreamView* cv = rv->get_time_axis_view().view ();
4067 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4068 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4069 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4071 framepos_t const pf = adjusted_current_frame (event);
4073 if (pf > rv->region()->position()) {
4074 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4077 show_verbose_cursor_time (pf);
4081 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4083 _primary->get_time_axis_view().hide_timestretch ();
4085 if (!movement_occurred) {
4089 if (last_pointer_frame() < _primary->region()->position()) {
4090 /* backwards drag of the left edge - not usable */
4094 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4096 float percentage = (double) newlen / (double) _primary->region()->length();
4098 #ifndef USE_RUBBERBAND
4099 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4100 if (_primary->region()->data_type() == DataType::AUDIO) {
4101 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4105 if (!_editor->get_selection().regions.empty()) {
4106 /* primary will already be included in the selection, and edit
4107 group shared editing will propagate selection across
4108 equivalent regions, so just use the current region
4112 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4113 error << _("An error occurred while executing time stretch operation") << endmsg;
4119 TimeFXDrag::aborted (bool)
4121 _primary->get_time_axis_view().hide_timestretch ();
4124 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4127 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4131 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4133 Drag::start_grab (event);
4137 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4139 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4143 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4145 if (movement_occurred && _editor->session()) {
4146 /* make sure we stop */
4147 _editor->session()->request_transport_speed (0.0);
4152 ScrubDrag::aborted (bool)
4157 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4161 , _original_pointer_time_axis (-1)
4162 , _time_selection_at_start (!_editor->get_selection().time.empty())
4164 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4166 if (_time_selection_at_start) {
4167 start_at_start = _editor->get_selection().time.start();
4168 end_at_start = _editor->get_selection().time.end_frame();
4173 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4175 if (_editor->session() == 0) {
4179 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4181 switch (_operation) {
4182 case CreateSelection:
4183 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4188 cursor = _editor->cursors()->selector;
4189 Drag::start_grab (event, cursor);
4192 case SelectionStartTrim:
4193 if (_editor->clicked_axisview) {
4194 _editor->clicked_axisview->order_selection_trims (_item, true);
4196 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4199 case SelectionEndTrim:
4200 if (_editor->clicked_axisview) {
4201 _editor->clicked_axisview->order_selection_trims (_item, false);
4203 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4207 Drag::start_grab (event, cursor);
4210 case SelectionExtend:
4211 Drag::start_grab (event, cursor);
4215 if (_operation == SelectionMove) {
4216 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4218 show_verbose_cursor_time (adjusted_current_frame (event));
4221 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4225 SelectionDrag::setup_pointer_frame_offset ()
4227 switch (_operation) {
4228 case CreateSelection:
4229 _pointer_frame_offset = 0;
4232 case SelectionStartTrim:
4234 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4237 case SelectionEndTrim:
4238 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4241 case SelectionExtend:
4247 SelectionDrag::motion (GdkEvent* event, bool first_move)
4249 framepos_t start = 0;
4251 framecnt_t length = 0;
4252 framecnt_t distance = 0;
4254 framepos_t const pending_position = adjusted_current_frame (event);
4256 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4260 switch (_operation) {
4261 case CreateSelection:
4263 framepos_t grab = grab_frame ();
4266 grab = adjusted_current_frame (event, false);
4267 if (grab < pending_position) {
4268 _editor->snap_to (grab, RoundDownMaybe);
4270 _editor->snap_to (grab, RoundUpMaybe);
4274 if (pending_position < grab) {
4275 start = pending_position;
4278 end = pending_position;
4282 /* first drag: Either add to the selection
4283 or create a new selection
4290 /* adding to the selection */
4291 _editor->set_selected_track_as_side_effect (Selection::Add);
4292 _editor->clicked_selection = _editor->selection->add (start, end);
4299 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4300 _editor->set_selected_track_as_side_effect (Selection::Set);
4303 _editor->clicked_selection = _editor->selection->set (start, end);
4307 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4308 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4309 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4311 _editor->selection->add (atest);
4315 /* select all tracks within the rectangle that we've marked out so far */
4316 TrackViewList new_selection;
4317 TrackViewList& all_tracks (_editor->track_views);
4319 ArdourCanvas::Coord const top = grab_y();
4320 ArdourCanvas::Coord const bottom = current_pointer_y();
4322 if (top >= 0 && bottom >= 0) {
4324 //first, find the tracks that are covered in the y range selection
4325 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4326 if ((*i)->covered_by_y_range (top, bottom)) {
4327 new_selection.push_back (*i);
4331 //now find any tracks that are GROUPED with the tracks we selected
4332 TrackViewList grouped_add = new_selection;
4333 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4334 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4335 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4336 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4337 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4338 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4339 grouped_add.push_back (*j);
4344 //now compare our list with the current selection, and add or remove as necessary
4345 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4346 TrackViewList tracks_to_add;
4347 TrackViewList tracks_to_remove;
4348 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4349 if ( !_editor->selection->tracks.contains ( *i ) )
4350 tracks_to_add.push_back ( *i );
4351 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4352 if ( !grouped_add.contains ( *i ) )
4353 tracks_to_remove.push_back ( *i );
4354 _editor->selection->add(tracks_to_add);
4355 _editor->selection->remove(tracks_to_remove);
4361 case SelectionStartTrim:
4363 start = _editor->selection->time[_editor->clicked_selection].start;
4364 end = _editor->selection->time[_editor->clicked_selection].end;
4366 if (pending_position > end) {
4369 start = pending_position;
4373 case SelectionEndTrim:
4375 start = _editor->selection->time[_editor->clicked_selection].start;
4376 end = _editor->selection->time[_editor->clicked_selection].end;
4378 if (pending_position < start) {
4381 end = pending_position;
4388 start = _editor->selection->time[_editor->clicked_selection].start;
4389 end = _editor->selection->time[_editor->clicked_selection].end;
4391 length = end - start;
4392 distance = pending_position - start;
4393 start = pending_position;
4394 _editor->snap_to (start);
4396 end = start + length;
4400 case SelectionExtend:
4405 switch (_operation) {
4407 if (_time_selection_at_start) {
4408 _editor->selection->move_time (distance);
4412 _editor->selection->replace (_editor->clicked_selection, start, end);
4416 if (_operation == SelectionMove) {
4417 show_verbose_cursor_time(start);
4419 show_verbose_cursor_time(pending_position);
4424 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4426 Session* s = _editor->session();
4428 _editor->begin_reversible_selection_op (_("Change Time Selection"));
4429 if (movement_occurred) {
4430 motion (event, false);
4431 /* XXX this is not object-oriented programming at all. ick */
4432 if (_editor->selection->time.consolidate()) {
4433 _editor->selection->TimeChanged ();
4436 /* XXX what if its a music time selection? */
4438 if ( s->get_play_range() && s->transport_rolling() ) {
4439 s->request_play_range (&_editor->selection->time, true);
4441 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4442 if (_operation == SelectionEndTrim)
4443 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4445 s->request_locate (_editor->get_selection().time.start());
4451 /* just a click, no pointer movement.
4454 if (_operation == SelectionExtend) {
4455 if (_time_selection_at_start) {
4456 framepos_t pos = adjusted_current_frame (event, false);
4457 framepos_t start = min (pos, start_at_start);
4458 framepos_t end = max (pos, end_at_start);
4459 _editor->selection->set (start, end);
4462 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4463 if (_editor->clicked_selection) {
4464 _editor->selection->remove (_editor->clicked_selection);
4467 if (!_editor->clicked_selection) {
4468 _editor->selection->clear_time();
4473 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4474 _editor->selection->set (_editor->clicked_axisview);
4477 if (s && s->get_play_range () && s->transport_rolling()) {
4478 s->request_stop (false, false);
4483 _editor->stop_canvas_autoscroll ();
4484 _editor->clicked_selection = 0;
4485 _editor->commit_reversible_selection_op ();
4489 SelectionDrag::aborted (bool)
4494 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4495 : Drag (e, i, false),
4499 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4501 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4502 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4503 physical_screen_height (_editor->get_window())));
4504 _drag_rect->hide ();
4506 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4507 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4511 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4513 if (_editor->session() == 0) {
4517 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4519 if (!_editor->temp_location) {
4520 _editor->temp_location = new Location (*_editor->session());
4523 switch (_operation) {
4524 case CreateSkipMarker:
4525 case CreateRangeMarker:
4526 case CreateTransportMarker:
4527 case CreateCDMarker:
4529 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4534 cursor = _editor->cursors()->selector;
4538 Drag::start_grab (event, cursor);
4540 show_verbose_cursor_time (adjusted_current_frame (event));
4544 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4546 framepos_t start = 0;
4548 ArdourCanvas::Rectangle *crect;
4550 switch (_operation) {
4551 case CreateSkipMarker:
4552 crect = _editor->range_bar_drag_rect;
4554 case CreateRangeMarker:
4555 crect = _editor->range_bar_drag_rect;
4557 case CreateTransportMarker:
4558 crect = _editor->transport_bar_drag_rect;
4560 case CreateCDMarker:
4561 crect = _editor->cd_marker_bar_drag_rect;
4564 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4569 framepos_t const pf = adjusted_current_frame (event);
4571 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4572 framepos_t grab = grab_frame ();
4573 _editor->snap_to (grab);
4575 if (pf < grab_frame()) {
4583 /* first drag: Either add to the selection
4584 or create a new selection.
4589 _editor->temp_location->set (start, end);
4593 update_item (_editor->temp_location);
4595 //_drag_rect->raise_to_top();
4601 _editor->temp_location->set (start, end);
4603 double x1 = _editor->sample_to_pixel (start);
4604 double x2 = _editor->sample_to_pixel (end);
4608 update_item (_editor->temp_location);
4611 show_verbose_cursor_time (pf);
4616 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4618 Location * newloc = 0;
4622 if (movement_occurred) {
4623 motion (event, false);
4626 switch (_operation) {
4627 case CreateSkipMarker:
4628 case CreateRangeMarker:
4629 case CreateCDMarker:
4631 XMLNode &before = _editor->session()->locations()->get_state();
4632 if (_operation == CreateSkipMarker) {
4633 _editor->begin_reversible_command (_("new skip marker"));
4634 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4635 flags = Location::IsRangeMarker | Location::IsSkip;
4636 _editor->range_bar_drag_rect->hide();
4637 } else if (_operation == CreateCDMarker) {
4638 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4639 _editor->begin_reversible_command (_("new CD marker"));
4640 flags = Location::IsRangeMarker | Location::IsCDMarker;
4641 _editor->cd_marker_bar_drag_rect->hide();
4643 _editor->begin_reversible_command (_("new skip marker"));
4644 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4645 flags = Location::IsRangeMarker;
4646 _editor->range_bar_drag_rect->hide();
4648 newloc = new Location (
4649 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4652 _editor->session()->locations()->add (newloc, true);
4653 XMLNode &after = _editor->session()->locations()->get_state();
4654 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4655 _editor->commit_reversible_command ();
4659 case CreateTransportMarker:
4660 // popup menu to pick loop or punch
4661 _editor->new_transport_marker_context_menu (&event->button, _item);
4667 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4669 if (_operation == CreateTransportMarker) {
4671 /* didn't drag, so just locate */
4673 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4675 } else if (_operation == CreateCDMarker) {
4677 /* didn't drag, but mark is already created so do
4680 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
4686 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4688 if (end == max_framepos) {
4689 end = _editor->session()->current_end_frame ();
4692 if (start == max_framepos) {
4693 start = _editor->session()->current_start_frame ();
4696 switch (_editor->mouse_mode) {
4698 /* find the two markers on either side and then make the selection from it */
4699 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4703 /* find the two markers on either side of the click and make the range out of it */
4704 _editor->selection->set (start, end);
4713 _editor->stop_canvas_autoscroll ();
4717 RangeMarkerBarDrag::aborted (bool)
4723 RangeMarkerBarDrag::update_item (Location* location)
4725 double const x1 = _editor->sample_to_pixel (location->start());
4726 double const x2 = _editor->sample_to_pixel (location->end());
4728 _drag_rect->set_x0 (x1);
4729 _drag_rect->set_x1 (x2);
4732 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4734 , _cumulative_dx (0)
4735 , _cumulative_dy (0)
4737 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4739 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4741 _region = &_primary->region_view ();
4742 _note_height = _region->midi_stream_view()->note_height ();
4746 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4748 Drag::start_grab (event);
4750 if (!(_was_selected = _primary->selected())) {
4752 /* tertiary-click means extend selection - we'll do that on button release,
4753 so don't add it here, because otherwise we make it hard to figure
4754 out the "extend-to" range.
4757 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4760 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4763 _region->note_selected (_primary, true);
4765 _region->unique_select (_primary);
4768 _editor->begin_reversible_selection_op(_("Select Note Press"));
4769 _editor->commit_reversible_selection_op();
4774 /** @return Current total drag x change in frames */
4776 NoteDrag::total_dx () const
4779 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4781 /* primary note time */
4782 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4784 /* new time of the primary note in session frames */
4785 frameoffset_t st = n + dx;
4787 framepos_t const rp = _region->region()->position ();
4789 /* prevent the note being dragged earlier than the region's position */
4792 /* snap and return corresponding delta */
4793 return _region->snap_frame_to_frame (st - rp) + rp - n;
4796 /** @return Current total drag y change in note number */
4798 NoteDrag::total_dy () const
4800 MidiStreamView* msv = _region->midi_stream_view ();
4801 double const y = _region->midi_view()->y_position ();
4802 /* new current note */
4803 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4805 n = max (msv->lowest_note(), n);
4806 n = min (msv->highest_note(), n);
4807 /* and work out delta */
4808 return n - msv->y_to_note (grab_y() - y);
4812 NoteDrag::motion (GdkEvent *, bool)
4814 /* Total change in x and y since the start of the drag */
4815 frameoffset_t const dx = total_dx ();
4816 int8_t const dy = total_dy ();
4818 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4819 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4820 double const tdy = -dy * _note_height - _cumulative_dy;
4823 _cumulative_dx += tdx;
4824 _cumulative_dy += tdy;
4826 int8_t note_delta = total_dy();
4828 _region->move_selection (tdx, tdy, note_delta);
4830 /* the new note value may be the same as the old one, but we
4831 * don't know what that means because the selection may have
4832 * involved more than one note and we might be doing something
4833 * odd with them. so show the note value anyway, always.
4837 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4839 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4840 (int) floor ((double)new_note));
4842 show_verbose_cursor_text (buf);
4847 NoteDrag::finished (GdkEvent* ev, bool moved)
4850 /* no motion - select note */
4852 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4853 _editor->current_mouse_mode() == Editing::MouseDraw) {
4855 bool changed = false;
4857 if (_was_selected) {
4858 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4860 _region->note_deselected (_primary);
4864 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4865 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4867 if (!extend && !add && _region->selection_size() > 1) {
4868 _region->unique_select (_primary);
4870 } else if (extend) {
4871 _region->note_selected (_primary, true, true);
4874 /* it was added during button press */
4879 _editor->begin_reversible_selection_op(_("Select Note Release"));
4880 _editor->commit_reversible_selection_op();
4884 _region->note_dropped (_primary, total_dx(), total_dy());
4889 NoteDrag::aborted (bool)
4894 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4895 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4896 : Drag (editor, atv->base_item ())
4898 , _y_origin (atv->y_position())
4899 , _nothing_to_drag (false)
4901 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4902 setup (atv->lines ());
4905 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
4906 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
4907 : Drag (editor, rv->get_canvas_group ())
4909 , _y_origin (rv->get_time_axis_view().y_position())
4910 , _nothing_to_drag (false)
4913 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4915 list<boost::shared_ptr<AutomationLine> > lines;
4917 AudioRegionView* audio_view;
4918 AutomationRegionView* automation_view;
4919 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
4920 lines.push_back (audio_view->get_gain_line ());
4921 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
4922 lines.push_back (automation_view->line ());
4925 error << _("Automation range drag created for invalid region type") << endmsg;
4931 /** @param lines AutomationLines to drag.
4932 * @param offset Offset from the session start to the points in the AutomationLines.
4935 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4937 /* find the lines that overlap the ranges being dragged */
4938 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4939 while (i != lines.end ()) {
4940 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4943 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4945 /* check this range against all the AudioRanges that we are using */
4946 list<AudioRange>::const_iterator k = _ranges.begin ();
4947 while (k != _ranges.end()) {
4948 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4954 /* add it to our list if it overlaps at all */
4955 if (k != _ranges.end()) {
4960 _lines.push_back (n);
4966 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4970 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4972 return 1.0 - ((global_y - _y_origin) / line->height());
4976 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
4978 const double v = list->eval(x);
4979 return _integral ? rint(v) : v;
4983 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4985 Drag::start_grab (event, cursor);
4987 /* Get line states before we start changing things */
4988 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4989 i->state = &i->line->get_state ();
4990 i->original_fraction = y_fraction (i->line, current_pointer_y());
4993 if (_ranges.empty()) {
4995 /* No selected time ranges: drag all points */
4996 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4997 uint32_t const N = i->line->npoints ();
4998 for (uint32_t j = 0; j < N; ++j) {
4999 i->points.push_back (i->line->nth (j));
5005 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5007 framecnt_t const half = (i->start + i->end) / 2;
5009 /* find the line that this audio range starts in */
5010 list<Line>::iterator j = _lines.begin();
5011 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5015 if (j != _lines.end()) {
5016 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5018 /* j is the line that this audio range starts in; fade into it;
5019 64 samples length plucked out of thin air.
5022 framepos_t a = i->start + 64;
5027 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5028 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5030 the_list->editor_add (p, value (the_list, p));
5031 the_list->editor_add (q, value (the_list, q));
5034 /* same thing for the end */
5037 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5041 if (j != _lines.end()) {
5042 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5044 /* j is the line that this audio range starts in; fade out of it;
5045 64 samples length plucked out of thin air.
5048 framepos_t b = i->end - 64;
5053 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5054 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5056 the_list->editor_add (p, value (the_list, p));
5057 the_list->editor_add (q, value (the_list, q));
5061 _nothing_to_drag = true;
5063 /* Find all the points that should be dragged and put them in the relevant
5064 points lists in the Line structs.
5067 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5069 uint32_t const N = i->line->npoints ();
5070 for (uint32_t j = 0; j < N; ++j) {
5072 /* here's a control point on this line */
5073 ControlPoint* p = i->line->nth (j);
5074 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5076 /* see if it's inside a range */
5077 list<AudioRange>::const_iterator k = _ranges.begin ();
5078 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5082 if (k != _ranges.end()) {
5083 /* dragging this point */
5084 _nothing_to_drag = false;
5085 i->points.push_back (p);
5091 if (_nothing_to_drag) {
5095 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5096 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5101 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5103 if (_nothing_to_drag) {
5107 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5108 float const f = y_fraction (l->line, current_pointer_y());
5109 /* we are ignoring x position for this drag, so we can just pass in anything */
5111 l->line->drag_motion (0, f, true, false, ignored);
5112 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5117 AutomationRangeDrag::finished (GdkEvent* event, bool)
5119 if (_nothing_to_drag) {
5123 motion (event, false);
5124 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5125 i->line->end_drag (false, 0);
5128 _editor->commit_reversible_command ();
5132 AutomationRangeDrag::aborted (bool)
5134 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5139 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5141 , initial_time_axis_view (itav)
5143 /* note that time_axis_view may be null if the regionview was created
5144 * as part of a copy operation.
5146 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5147 layer = v->region()->layer ();
5148 initial_y = v->get_canvas_group()->position().y;
5149 initial_playlist = v->region()->playlist ();
5150 initial_position = v->region()->position ();
5151 initial_end = v->region()->position () + v->region()->length ();
5154 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5155 : Drag (e, i->canvas_item ())
5158 , _cumulative_dx (0)
5160 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5161 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5166 PatchChangeDrag::motion (GdkEvent* ev, bool)
5168 framepos_t f = adjusted_current_frame (ev);
5169 boost::shared_ptr<Region> r = _region_view->region ();
5170 f = max (f, r->position ());
5171 f = min (f, r->last_frame ());
5173 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5174 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5175 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5176 _cumulative_dx = dxu;
5180 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5182 if (!movement_occurred) {
5186 boost::shared_ptr<Region> r (_region_view->region ());
5187 framepos_t f = adjusted_current_frame (ev);
5188 f = max (f, r->position ());
5189 f = min (f, r->last_frame ());
5191 _region_view->move_patch_change (
5193 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5198 PatchChangeDrag::aborted (bool)
5200 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5204 PatchChangeDrag::setup_pointer_frame_offset ()
5206 boost::shared_ptr<Region> region = _region_view->region ();
5207 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5210 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5211 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5218 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5220 _region_view->update_drag_selection (
5222 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5226 MidiRubberbandSelectDrag::deselect_things ()
5231 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5232 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5235 _vertical_only = true;
5239 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5241 double const y = _region_view->midi_view()->y_position ();
5243 y1 = max (0.0, y1 - y);
5244 y2 = max (0.0, y2 - y);
5246 _region_view->update_vertical_drag_selection (
5249 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5254 MidiVerticalSelectDrag::deselect_things ()
5259 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5260 : RubberbandSelectDrag (e, i)
5266 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5268 if (drag_in_progress) {
5269 /* We just want to select things at the end of the drag, not during it */
5273 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5275 _editor->begin_reversible_selection_op (_("rubberband selection"));
5277 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5279 _editor->commit_reversible_selection_op ();
5283 EditorRubberbandSelectDrag::deselect_things ()
5285 _editor->begin_reversible_selection_op (_("Clear Selection (rubberband)"));
5287 _editor->selection->clear_tracks();
5288 _editor->selection->clear_regions();
5289 _editor->selection->clear_points ();
5290 _editor->selection->clear_lines ();
5291 _editor->selection->clear_midi_notes ();
5293 _editor->commit_reversible_selection_op();
5296 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5301 _note[0] = _note[1] = 0;
5304 NoteCreateDrag::~NoteCreateDrag ()
5310 NoteCreateDrag::grid_frames (framepos_t t) const
5313 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5315 grid_beats = Evoral::Beats(1);
5318 return _region_view->region_beats_to_region_frames (grid_beats);
5322 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5324 Drag::start_grab (event, cursor);
5326 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5328 framepos_t pf = _drags->current_pointer_frame ();
5329 framecnt_t const g = grid_frames (pf);
5331 /* Hack so that we always snap to the note that we are over, instead of snapping
5332 to the next one if we're more than halfway through the one we're over.
5334 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5338 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5339 _note[1] = _note[0];
5341 MidiStreamView* sv = _region_view->midi_stream_view ();
5342 double const x = _editor->sample_to_pixel (_note[0]);
5343 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5345 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5346 _drag_rect->set_outline_all ();
5347 _drag_rect->set_outline_color (0xffffff99);
5348 _drag_rect->set_fill_color (0xffffff66);
5352 NoteCreateDrag::motion (GdkEvent* event, bool)
5354 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5355 double const x0 = _editor->sample_to_pixel (_note[0]);
5356 double const x1 = _editor->sample_to_pixel (_note[1]);
5357 _drag_rect->set_x0 (std::min(x0, x1));
5358 _drag_rect->set_x1 (std::max(x0, x1));
5362 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5364 if (!had_movement) {
5368 framepos_t const start = min (_note[0], _note[1]);
5369 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5371 framecnt_t const g = grid_frames (start);
5372 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5374 if (_editor->snap_mode() == SnapNormal && length < g) {
5378 Evoral::Beats length_beats = max (
5379 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5381 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5385 NoteCreateDrag::y_to_region (double y) const
5388 _region_view->get_canvas_group()->canvas_to_item (x, y);
5393 NoteCreateDrag::aborted (bool)
5398 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5403 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5407 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5409 Drag::start_grab (event, cursor);
5413 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5419 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5422 distance = _drags->current_pointer_x() - grab_x();
5423 len = ar->fade_in()->back()->when;
5425 distance = grab_x() - _drags->current_pointer_x();
5426 len = ar->fade_out()->back()->when;
5429 /* how long should it be ? */
5431 new_length = len + _editor->pixel_to_sample (distance);
5433 /* now check with the region that this is legal */
5435 new_length = ar->verify_xfade_bounds (new_length, start);
5438 arv->reset_fade_in_shape_width (ar, new_length);
5440 arv->reset_fade_out_shape_width (ar, new_length);
5445 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5451 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5454 distance = _drags->current_pointer_x() - grab_x();
5455 len = ar->fade_in()->back()->when;
5457 distance = grab_x() - _drags->current_pointer_x();
5458 len = ar->fade_out()->back()->when;
5461 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5463 _editor->begin_reversible_command ("xfade trim");
5464 ar->playlist()->clear_owned_changes ();
5467 ar->set_fade_in_length (new_length);
5469 ar->set_fade_out_length (new_length);
5472 /* Adjusting the xfade may affect other regions in the playlist, so we need
5473 to get undo Commands from the whole playlist rather than just the
5477 vector<Command*> cmds;
5478 ar->playlist()->rdiff (cmds);
5479 _editor->session()->add_commands (cmds);
5480 _editor->commit_reversible_command ();
5485 CrossfadeEdgeDrag::aborted (bool)
5488 arv->redraw_start_xfade ();
5490 arv->redraw_end_xfade ();
5494 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5495 : Drag (e, item, true)
5496 , line (new EditorCursor (*e))
5498 line->set_position (pos);
5502 RegionCutDrag::~RegionCutDrag ()
5508 RegionCutDrag::motion (GdkEvent*, bool)
5510 framepos_t where = _drags->current_pointer_frame();
5511 _editor->snap_to (where);
5513 line->set_position (where);
5517 RegionCutDrag::finished (GdkEvent*, bool)
5519 _editor->get_track_canvas()->canvas()->re_enter();
5521 framepos_t pos = _drags->current_pointer_frame();
5525 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5531 _editor->split_regions_at (pos, rs);
5535 RegionCutDrag::aborted (bool)