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/scroll_group.h"
48 #include "audio_region_view.h"
49 #include "midi_region_view.h"
50 #include "ardour_ui.h"
51 #include "gui_thread.h"
52 #include "control_point.h"
53 #include "region_gain_line.h"
54 #include "editor_drag.h"
55 #include "audio_time_axis.h"
56 #include "midi_time_axis.h"
57 #include "selection.h"
58 #include "midi_selection.h"
59 #include "automation_time_axis.h"
61 #include "editor_cursors.h"
62 #include "mouse_cursors.h"
63 #include "note_base.h"
64 #include "patch_change.h"
65 #include "verbose_cursor.h"
68 using namespace ARDOUR;
71 using namespace Gtkmm2ext;
72 using namespace Editing;
73 using namespace ArdourCanvas;
75 using Gtkmm2ext::Keyboard;
77 double ControlPointDrag::_zero_gain_fraction = -1.0;
79 DragManager::DragManager (Editor* e)
82 , _current_pointer_frame (0)
86 DragManager::~DragManager ()
91 /** Call abort for each active drag */
97 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
102 if (!_drags.empty ()) {
103 _editor->set_follow_playhead (_old_follow_playhead, false);
112 DragManager::add (Drag* d)
114 d->set_manager (this);
115 _drags.push_back (d);
119 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
121 d->set_manager (this);
122 _drags.push_back (d);
127 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
129 /* Prevent follow playhead during the drag to be nice to the user */
130 _old_follow_playhead = _editor->follow_playhead ();
131 _editor->set_follow_playhead (false);
133 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
135 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
136 (*i)->start_grab (e, c);
140 /** Call end_grab for each active drag.
141 * @return true if any drag reported movement having occurred.
144 DragManager::end_grab (GdkEvent* e)
149 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
150 bool const t = (*i)->end_grab (e);
161 _editor->set_follow_playhead (_old_follow_playhead, false);
167 DragManager::mark_double_click ()
169 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
170 (*i)->set_double_click (true);
175 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
179 /* calling this implies that we expect the event to have canvas
182 * Can we guarantee that this is true?
185 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
187 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
188 bool const t = (*i)->motion_handler (e, from_autoscroll);
189 /* run all handlers; return true if at least one of them
190 returns true (indicating that the event has been handled).
202 DragManager::have_item (ArdourCanvas::Item* i) const
204 list<Drag*>::const_iterator j = _drags.begin ();
205 while (j != _drags.end() && (*j)->item () != i) {
209 return j != _drags.end ();
212 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
215 , _pointer_frame_offset (0)
216 , _trackview_only (trackview_only)
217 , _move_threshold_passed (false)
218 , _was_double_click (false)
219 , _raw_grab_frame (0)
221 , _last_pointer_frame (0)
227 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
240 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
242 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
244 if (Keyboard::is_button2_event (&event->button)) {
245 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
246 _y_constrained = true;
247 _x_constrained = false;
249 _y_constrained = false;
250 _x_constrained = true;
253 _x_constrained = false;
254 _y_constrained = false;
257 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
258 setup_pointer_frame_offset ();
259 _grab_frame = adjusted_frame (_raw_grab_frame, event);
260 _last_pointer_frame = _grab_frame;
261 _last_pointer_x = _grab_x;
263 if (_trackview_only) {
264 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
267 _last_pointer_y = _grab_y;
272 /* CAIROCANVAS need a variant here that passes *cursor */
274 _editor->push_canvas_cursor (cursor);
277 if (_editor->session() && _editor->session()->transport_rolling()) {
280 _was_rolling = false;
283 switch (_editor->snap_type()) {
284 case SnapToRegionStart:
285 case SnapToRegionEnd:
286 case SnapToRegionSync:
287 case SnapToRegionBoundary:
288 _editor->build_region_boundary_cache ();
295 /** Call to end a drag `successfully'. Ungrabs item and calls
296 * subclass' finished() method.
298 * @param event GDK event, or 0.
299 * @return true if some movement occurred, otherwise false.
302 Drag::end_grab (GdkEvent* event)
304 _editor->stop_canvas_autoscroll ();
308 finished (event, _move_threshold_passed);
310 _editor->verbose_cursor()->hide ();
311 _editor->pop_canvas_cursor ();
313 return _move_threshold_passed;
317 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
321 if (f > _pointer_frame_offset) {
322 pos = f - _pointer_frame_offset;
326 _editor->snap_to_with_modifier (pos, event);
333 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
335 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
339 Drag::current_pointer_y () const
341 if (!_trackview_only) {
342 return _drags->current_pointer_y ();
345 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
349 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
351 /* check to see if we have moved in any way that matters since the last motion event */
352 if (_move_threshold_passed &&
353 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
354 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
358 pair<framecnt_t, int> const threshold = move_threshold ();
360 bool const old_move_threshold_passed = _move_threshold_passed;
362 if (!from_autoscroll && !_move_threshold_passed) {
364 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
365 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
367 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
370 if (active (_editor->mouse_mode) && _move_threshold_passed) {
372 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
373 if (!from_autoscroll) {
374 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
377 if (!_editor->autoscroll_active() || from_autoscroll) {
378 motion (event, _move_threshold_passed != old_move_threshold_passed);
380 _last_pointer_x = _drags->current_pointer_x ();
381 _last_pointer_y = current_pointer_y ();
382 _last_pointer_frame = adjusted_current_frame (event);
392 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
400 aborted (_move_threshold_passed);
402 _editor->stop_canvas_autoscroll ();
403 _editor->verbose_cursor()->hide ();
407 Drag::show_verbose_cursor_time (framepos_t frame)
409 _editor->verbose_cursor()->set_time (frame);
410 _editor->verbose_cursor()->show ();
414 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
416 _editor->verbose_cursor()->set_duration (start, end);
417 _editor->verbose_cursor()->show ();
421 Drag::show_verbose_cursor_text (string const & text)
423 _editor->verbose_cursor()->set (text);
424 _editor->verbose_cursor()->show ();
427 boost::shared_ptr<Region>
428 Drag::add_midi_region (MidiTimeAxisView* view)
430 if (_editor->session()) {
431 const TempoMap& map (_editor->session()->tempo_map());
432 framecnt_t pos = grab_frame();
433 const Meter& m = map.meter_at (pos);
434 /* not that the frame rate used here can be affected by pull up/down which
437 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
438 return view->add_region (grab_frame(), len, true);
441 return boost::shared_ptr<Region>();
444 struct EditorOrderTimeAxisViewSorter {
445 bool operator() (TimeAxisView* a, TimeAxisView* b) {
446 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
447 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
449 return ra->route()->order_key () < rb->route()->order_key ();
453 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
457 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
459 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
460 as some of the regions we are dragging may be on such tracks.
463 TrackViewList track_views = _editor->track_views;
464 track_views.sort (EditorOrderTimeAxisViewSorter ());
466 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
467 _time_axis_views.push_back (*i);
469 TimeAxisView::Children children_list = (*i)->get_child_list ();
470 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
471 _time_axis_views.push_back (j->get());
475 /* the list of views can be empty at this point if this is a region list-insert drag
478 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
479 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
482 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
486 RegionDrag::region_going_away (RegionView* v)
488 list<DraggingView>::iterator i = _views.begin ();
489 while (i != _views.end() && i->view != v) {
493 if (i != _views.end()) {
498 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
499 * or -1 if it is not found.
502 RegionDrag::find_time_axis_view (TimeAxisView* t) const
505 int const N = _time_axis_views.size ();
506 while (i < N && _time_axis_views[i] != t) {
517 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
518 : RegionDrag (e, i, p, v)
521 , _last_pointer_time_axis_view (0)
522 , _last_pointer_layer (0)
524 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
528 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
530 Drag::start_grab (event, cursor);
532 show_verbose_cursor_time (_last_frame_position);
534 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
536 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
537 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
542 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
544 /* compute the amount of pointer motion in frames, and where
545 the region would be if we moved it by that much.
547 *pending_region_position = adjusted_current_frame (event);
549 framepos_t sync_frame;
550 framecnt_t sync_offset;
553 sync_offset = _primary->region()->sync_offset (sync_dir);
555 /* we don't handle a sync point that lies before zero.
557 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
559 sync_frame = *pending_region_position + (sync_dir*sync_offset);
561 _editor->snap_to_with_modifier (sync_frame, event);
563 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
566 *pending_region_position = _last_frame_position;
569 if (*pending_region_position > max_framepos - _primary->region()->length()) {
570 *pending_region_position = _last_frame_position;
575 /* in locked edit mode, reverse the usual meaning of _x_constrained */
576 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
578 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
580 /* x movement since last time (in pixels) */
581 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
583 /* total x movement */
584 framecnt_t total_dx = *pending_region_position;
585 if (regions_came_from_canvas()) {
586 total_dx = total_dx - grab_frame ();
589 /* check that no regions have gone off the start of the session */
590 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
591 if ((i->view->region()->position() + total_dx) < 0) {
593 *pending_region_position = _last_frame_position;
604 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
606 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
607 int const n = i->time_axis_view + delta_track;
608 if (n < 0 || n >= int (_time_axis_views.size())) {
609 /* off the top or bottom track */
613 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
614 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
615 /* not a track, or the wrong type */
619 double const l = i->layer + delta_layer;
621 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
622 mode to allow the user to place a region below another on layer 0.
624 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
625 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
626 If it has, the layers will be munged later anyway, so it's ok.
632 /* all regions being dragged are ok with this change */
637 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
639 double delta_layer = 0;
640 int delta_time_axis_view = 0;
642 assert (!_views.empty ());
644 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
646 /* Find the TimeAxisView that the pointer is now over */
647 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
648 TimeAxisView* tv = r.first;
650 if (tv && tv->view()) {
651 double layer = r.second;
653 if (first_move && tv->view()->layer_display() == Stacked) {
654 tv->view()->set_layer_display (Expanded);
657 /* Here's the current pointer position in terms of time axis view and layer */
658 int const current_pointer_time_axis_view = find_time_axis_view (tv);
659 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
661 /* Work out the change in y */
663 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
664 delta_layer = current_pointer_layer - _last_pointer_layer;
667 /* Work out the change in x */
668 framepos_t pending_region_position;
669 double const x_delta = compute_x_delta (event, &pending_region_position);
670 _last_frame_position = pending_region_position;
672 /* Verify change in y */
673 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
674 /* this y movement is not allowed, so do no y movement this time */
675 delta_time_axis_view = 0;
679 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
680 /* haven't reached next snap point, and we're not switching
681 trackviews nor layers. nothing to do.
686 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
688 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
690 RegionView* rv = i->view;
692 if (rv->region()->locked() || rv->region()->video_locked()) {
699 /* reparent the regionview into a group above all
703 ArdourCanvas::Item* rvg = rv->get_canvas_group();
704 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
705 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
706 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
707 /* move the item so that it continues to appear at the
708 same location now that its parent has changed.
710 rvg->move (rv_canvas_offset - dmg_canvas_offset);
713 /* If we have moved tracks, we'll fudge the layer delta so that the
714 region gets moved back onto layer 0 on its new track; this avoids
715 confusion when dragging regions from non-zero layers onto different
718 double this_delta_layer = delta_layer;
719 if (delta_time_axis_view != 0) {
720 this_delta_layer = - i->layer;
727 if (i->time_axis_view >= 0) {
728 track_index = i->time_axis_view + delta_time_axis_view;
730 track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
733 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
737 /* The TimeAxisView that this region is now over */
738 TimeAxisView* current_tv = _time_axis_views[track_index];
740 /* Ensure it is moved from stacked -> expanded if appropriate */
741 if (current_tv->view()->layer_display() == Stacked) {
742 current_tv->view()->set_layer_display (Expanded);
745 /* We're only allowed to go -ve in layer on Expanded views */
746 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
747 this_delta_layer = - i->layer;
751 rv->set_height (current_tv->view()->child_height ());
753 /* Update show/hidden status as the region view may have come from a hidden track,
754 or have moved to one.
756 if (current_tv->hidden ()) {
757 rv->get_canvas_group()->hide ();
759 rv->get_canvas_group()->show ();
762 /* Update the DraggingView */
763 i->time_axis_view = track_index;
764 i->layer += this_delta_layer;
767 _editor->mouse_brush_insert_region (rv, pending_region_position);
771 /* Get the y coordinate of the top of the track that this region is now over */
772 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
774 /* And adjust for the layer that it should be on */
775 StreamView* cv = current_tv->view ();
776 switch (cv->layer_display ()) {
780 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
783 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
787 /* need to get the parent of the regionview
788 * canvas group and get its position in
789 * equivalent coordinate space as the trackview
790 * we are now dragging over.
793 /* Now move the region view */
794 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->canvas_origin().y);
798 /* Only move the region into the empty dropzone at the bottom if the pointer
802 if (current_pointer_y() >= 0) {
804 Coord last_track_bottom_edge;
805 if (!_time_axis_views.empty()) {
806 TimeAxisView* last = _time_axis_views.back();
807 last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
809 last_track_bottom_edge = 0;
812 rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y);
813 i->time_axis_view = -1;
817 } /* foreach region */
819 _total_x_delta += x_delta;
821 if (x_delta != 0 && !_brushing) {
822 show_verbose_cursor_time (_last_frame_position);
825 _last_pointer_time_axis_view += delta_time_axis_view;
826 _last_pointer_layer += delta_layer;
830 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
832 if (_copy && first_move) {
834 /* duplicate the regionview(s) and region(s) */
836 list<DraggingView> new_regionviews;
838 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
840 RegionView* rv = i->view;
841 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
842 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
844 const boost::shared_ptr<const Region> original = rv->region();
845 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
846 region_copy->set_position (original->position());
850 boost::shared_ptr<AudioRegion> audioregion_copy
851 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
853 nrv = new AudioRegionView (*arv, audioregion_copy);
855 boost::shared_ptr<MidiRegion> midiregion_copy
856 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
857 nrv = new MidiRegionView (*mrv, midiregion_copy);
862 nrv->get_canvas_group()->show ();
863 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
865 /* swap _primary to the copy */
867 if (rv == _primary) {
871 /* ..and deselect the one we copied */
873 rv->set_selected (false);
876 if (!new_regionviews.empty()) {
878 /* reflect the fact that we are dragging the copies */
880 _views = new_regionviews;
882 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
886 RegionMotionDrag::motion (event, first_move);
890 RegionMotionDrag::finished (GdkEvent *, bool)
892 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
897 if ((*i)->view()->layer_display() == Expanded) {
898 (*i)->view()->set_layer_display (Stacked);
904 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
906 RegionMotionDrag::finished (ev, movement_occurred);
908 if (!movement_occurred) {
912 if (was_double_click() && !_views.empty()) {
913 DraggingView dv = _views.front();
914 dv.view->show_region_editor ();
921 /* reverse this here so that we have the correct logic to finalize
925 if (Config->get_edit_mode() == Lock) {
926 _x_constrained = !_x_constrained;
929 assert (!_views.empty ());
931 /* We might have hidden region views so that they weren't visible during the drag
932 (when they have been reparented). Now everything can be shown again, as region
933 views are back in their track parent groups.
935 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
936 i->view->get_canvas_group()->show ();
939 bool const changed_position = (_last_frame_position != _primary->region()->position());
940 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
941 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
961 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
965 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
967 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
972 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
973 list<boost::shared_ptr<AudioTrack> > audio_tracks;
974 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
975 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
977 rtav->set_height (original->current_height());
981 ChanCount one_midi_port (DataType::MIDI, 1);
982 list<boost::shared_ptr<MidiTrack> > midi_tracks;
983 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
984 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
986 rtav->set_height (original->current_height());
991 error << _("Could not create new track after region placed in the drop zone") << endmsg;
997 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
999 RegionSelection new_views;
1000 PlaylistSet modified_playlists;
1001 RouteTimeAxisView* new_time_axis_view = 0;
1004 /* all changes were made during motion event handlers */
1006 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1010 _editor->commit_reversible_command ();
1014 if (_x_constrained) {
1015 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1017 _editor->begin_reversible_command (Operations::region_copy);
1020 /* insert the regions into their new playlists */
1021 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1023 RouteTimeAxisView* dest_rtv = 0;
1025 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1031 if (changed_position && !_x_constrained) {
1032 where = i->view->region()->position() - drag_delta;
1034 where = i->view->region()->position();
1037 if (i->time_axis_view < 0) {
1038 if (!new_time_axis_view) {
1039 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1041 dest_rtv = new_time_axis_view;
1043 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1046 if (dest_rtv != 0) {
1047 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1048 if (new_view != 0) {
1049 new_views.push_back (new_view);
1053 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1054 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1057 list<DraggingView>::const_iterator next = i;
1063 /* If we've created new regions either by copying or moving
1064 to a new track, we want to replace the old selection with the new ones
1067 if (new_views.size() > 0) {
1068 _editor->selection->set (new_views);
1071 /* write commands for the accumulated diffs for all our modified playlists */
1072 add_stateful_diff_commands_for_playlists (modified_playlists);
1074 _editor->commit_reversible_command ();
1078 RegionMoveDrag::finished_no_copy (
1079 bool const changed_position,
1080 bool const changed_tracks,
1081 framecnt_t const drag_delta
1084 RegionSelection new_views;
1085 PlaylistSet modified_playlists;
1086 PlaylistSet frozen_playlists;
1087 set<RouteTimeAxisView*> views_to_update;
1088 RouteTimeAxisView* new_time_axis_view = 0;
1091 /* all changes were made during motion event handlers */
1092 _editor->commit_reversible_command ();
1096 if (_x_constrained) {
1097 _editor->begin_reversible_command (_("fixed time region drag"));
1099 _editor->begin_reversible_command (Operations::region_drag);
1102 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1104 RegionView* rv = i->view;
1105 RouteTimeAxisView* dest_rtv = 0;
1107 if (rv->region()->locked() || rv->region()->video_locked()) {
1112 if (i->time_axis_view < 0) {
1113 if (!new_time_axis_view) {
1114 new_time_axis_view = create_destination_time_axis (rv->region(), i->initial_time_axis_view);
1116 dest_rtv = new_time_axis_view;
1118 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1123 double const dest_layer = i->layer;
1125 views_to_update.insert (dest_rtv);
1129 if (changed_position && !_x_constrained) {
1130 where = rv->region()->position() - drag_delta;
1132 where = rv->region()->position();
1135 if (changed_tracks) {
1137 /* insert into new playlist */
1139 RegionView* new_view = insert_region_into_playlist (
1140 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1143 if (new_view == 0) {
1148 new_views.push_back (new_view);
1150 /* remove from old playlist */
1152 /* the region that used to be in the old playlist is not
1153 moved to the new one - we use a copy of it. as a result,
1154 any existing editor for the region should no longer be
1157 rv->hide_region_editor();
1160 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1164 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1166 /* this movement may result in a crossfade being modified, or a layering change,
1167 so we need to get undo data from the playlist as well as the region.
1170 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1172 playlist->clear_changes ();
1175 rv->region()->clear_changes ();
1178 motion on the same track. plonk the previously reparented region
1179 back to its original canvas group (its streamview).
1180 No need to do anything for copies as they are fake regions which will be deleted.
1183 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1184 rv->get_canvas_group()->set_y_position (i->initial_y);
1187 /* just change the model */
1188 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1189 playlist->set_layer (rv->region(), dest_layer);
1192 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1194 r = frozen_playlists.insert (playlist);
1197 playlist->freeze ();
1200 rv->region()->set_position (where);
1202 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1205 if (changed_tracks) {
1207 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1208 was selected in all of them, then removing it from a playlist will have removed all
1209 trace of it from _views (i.e. there were N regions selected, we removed 1,
1210 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1211 corresponding regionview, and _views is now empty).
1213 This could have invalidated any and all iterators into _views.
1215 The heuristic we use here is: if the region selection is empty, break out of the loop
1216 here. if the region selection is not empty, then restart the loop because we know that
1217 we must have removed at least the region(view) we've just been working on as well as any
1218 that we processed on previous iterations.
1220 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1221 we can just iterate.
1225 if (_views.empty()) {
1236 /* If we've created new regions either by copying or moving
1237 to a new track, we want to replace the old selection with the new ones
1240 if (new_views.size() > 0) {
1241 _editor->selection->set (new_views);
1244 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1248 /* write commands for the accumulated diffs for all our modified playlists */
1249 add_stateful_diff_commands_for_playlists (modified_playlists);
1251 _editor->commit_reversible_command ();
1253 /* We have futzed with the layering of canvas items on our streamviews.
1254 If any region changed layer, this will have resulted in the stream
1255 views being asked to set up their region views, and all will be well.
1256 If not, we might now have badly-ordered region views. Ask the StreamViews
1257 involved to sort themselves out, just in case.
1260 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1261 (*i)->view()->playlist_layered ((*i)->track ());
1265 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1266 * @param region Region to remove.
1267 * @param playlist playlist To remove from.
1268 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1269 * that clear_changes () is only called once per playlist.
1272 RegionMoveDrag::remove_region_from_playlist (
1273 boost::shared_ptr<Region> region,
1274 boost::shared_ptr<Playlist> playlist,
1275 PlaylistSet& modified_playlists
1278 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1281 playlist->clear_changes ();
1284 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1288 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1289 * clearing the playlist's diff history first if necessary.
1290 * @param region Region to insert.
1291 * @param dest_rtv Destination RouteTimeAxisView.
1292 * @param dest_layer Destination layer.
1293 * @param where Destination position.
1294 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1295 * that clear_changes () is only called once per playlist.
1296 * @return New RegionView, or 0 if no insert was performed.
1299 RegionMoveDrag::insert_region_into_playlist (
1300 boost::shared_ptr<Region> region,
1301 RouteTimeAxisView* dest_rtv,
1304 PlaylistSet& modified_playlists
1307 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1308 if (!dest_playlist) {
1312 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1313 _new_region_view = 0;
1314 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1316 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1317 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1319 dest_playlist->clear_changes ();
1322 dest_playlist->add_region (region, where);
1324 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1325 dest_playlist->set_layer (region, dest_layer);
1330 assert (_new_region_view);
1332 return _new_region_view;
1336 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1338 _new_region_view = rv;
1342 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1344 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1345 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1347 _editor->session()->add_command (c);
1356 RegionMoveDrag::aborted (bool movement_occurred)
1360 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1367 RegionMotionDrag::aborted (movement_occurred);
1372 RegionMotionDrag::aborted (bool)
1374 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1376 StreamView* sview = (*i)->view();
1379 if (sview->layer_display() == Expanded) {
1380 sview->set_layer_display (Stacked);
1385 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1386 RegionView* rv = i->view;
1387 TimeAxisView* tv = &(rv->get_time_axis_view ());
1388 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1390 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1391 rv->get_canvas_group()->set_y_position (0);
1393 rv->move (-_total_x_delta, 0);
1394 rv->set_height (rtv->view()->child_height ());
1398 /** @param b true to brush, otherwise false.
1399 * @param c true to make copies of the regions being moved, otherwise false.
1401 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1402 : RegionMotionDrag (e, i, p, v, b),
1405 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1408 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1409 if (rtv && rtv->is_track()) {
1410 speed = rtv->track()->speed ();
1413 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1417 RegionMoveDrag::setup_pointer_frame_offset ()
1419 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1422 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1423 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1425 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1427 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1428 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1430 _primary = v->view()->create_region_view (r, false, false);
1432 _primary->get_canvas_group()->show ();
1433 _primary->set_position (pos, 0);
1434 _views.push_back (DraggingView (_primary, this, v));
1436 _last_frame_position = pos;
1438 _item = _primary->get_canvas_group ();
1442 RegionInsertDrag::finished (GdkEvent *, bool)
1444 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1446 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1447 _primary->get_canvas_group()->set_y_position (0);
1449 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1451 _editor->begin_reversible_command (Operations::insert_region);
1452 playlist->clear_changes ();
1453 playlist->add_region (_primary->region (), _last_frame_position);
1455 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1456 if (Config->get_edit_mode() == Ripple) {
1457 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1460 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1461 _editor->commit_reversible_command ();
1469 RegionInsertDrag::aborted (bool)
1476 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1477 : RegionMoveDrag (e, i, p, v, false, false)
1479 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1482 struct RegionSelectionByPosition {
1483 bool operator() (RegionView*a, RegionView* b) {
1484 return a->region()->position () < b->region()->position();
1489 RegionSpliceDrag::motion (GdkEvent* event, bool)
1491 /* Which trackview is this ? */
1493 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1494 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1496 /* The region motion is only processed if the pointer is over
1500 if (!tv || !tv->is_track()) {
1501 /* To make sure we hide the verbose canvas cursor when the mouse is
1502 not held over an audio track.
1504 _editor->verbose_cursor()->hide ();
1507 _editor->verbose_cursor()->show ();
1512 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1518 RegionSelection copy;
1519 _editor->selection->regions.by_position(copy);
1521 framepos_t const pf = adjusted_current_frame (event);
1523 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1525 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1531 boost::shared_ptr<Playlist> playlist;
1533 if ((playlist = atv->playlist()) == 0) {
1537 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1542 if (pf < (*i)->region()->last_frame() + 1) {
1546 if (pf > (*i)->region()->first_frame()) {
1552 playlist->shuffle ((*i)->region(), dir);
1557 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1559 RegionMoveDrag::finished (event, movement_occurred);
1563 RegionSpliceDrag::aborted (bool)
1573 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1575 RegionSelection to_ripple;
1576 TrackViewList tracks;
1577 tracks.push_back (tav);
1578 _editor->get_regions_after (to_ripple, where, tracks);
1580 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1581 if (!exclude.contains (*i)) {
1582 // the selection has already been added to _views
1584 if (drag_in_progress) {
1585 // do the same things that RegionMotionDrag::motion does when first_move
1586 // is true for the region views that we're adding to _views this time
1589 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1590 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1591 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1592 rvg->reparent (_editor->_drag_motion_group);
1594 // XXX without the following, things jump in the y direction during drags
1595 // with it, they jump in the x direction
1596 // so we need to do the move in the y direction only
1597 // rvg->move (rv_canvas_offset - dmg_canvas_offset);
1598 std::cerr << "rv_canvas_offset = " << rv_canvas_offset << ", dmg_canvas_offset = " << dmg_canvas_offset << std::endl;
1599 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1604 _views.push_back (DraggingView (*i, this, tav));
1610 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1613 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1614 // we added all the regions after the selection
1616 std::list<DraggingView>::iterator to_erase = i++;
1617 if (!_editor->selection->regions.contains (to_erase->view)) {
1618 // restore the non-selected regions to their original playlist & positions,
1619 // and then ripple them back by the length of the regions that were dragged away
1620 // do the same things as RegionMotionDrag::aborted
1622 RegionView *rv = to_erase->view;
1623 TimeAxisView* tv = &(rv->get_time_axis_view ());
1624 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1627 std::cerr << "rtv = " << rtv->name() << std::endl;
1629 // plonk them back onto their own track
1630 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1631 rv->get_canvas_group()->set_y_position (0);
1635 // move the underlying region to match the view
1636 rv->region()->set_position (rv->region()->position() + amount);
1638 // restore the view to match the underlying region's original position
1639 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
1642 rv->set_height (rtv->view()->child_height ());
1643 _views.erase (to_erase);
1649 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const
1651 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) {
1653 return allow_moves_across_tracks;
1661 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1662 : RegionMoveDrag (e, i, p, v, false, false)
1664 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
1665 // compute length of selection
1666 RegionSelection selected_regions = _editor->selection->regions;
1667 selection_length = selected_regions.end_frame() - selected_regions.start();
1669 // we'll only allow dragging to another track in ripple mode if all the regions
1670 // being dragged start off on the same track
1671 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
1674 exclude = new RegionList;
1675 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
1676 exclude->push_back((*i)->region());
1679 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
1680 RegionSelection copy;
1681 selected_regions.by_position(copy); // get selected regions sorted by position into copy
1683 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
1684 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1686 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1687 // find ripple start point on each applicable playlist
1688 RegionView *first_selected_on_this_track = NULL;
1689 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1690 if ((*i)->region()->playlist() == (*pi)) {
1691 // region is on this playlist - it's the first, because they're sorted
1692 first_selected_on_this_track = *i;
1696 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
1697 add_all_after_to_views (
1698 &first_selected_on_this_track->get_time_axis_view(),
1699 first_selected_on_this_track->region()->position() + first_selected_on_this_track->region()->length(),
1700 selected_regions, false);
1703 if (allow_moves_across_tracks) {
1704 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
1712 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
1714 /* Which trackview is this ? */
1716 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1717 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1719 /* The region motion is only processed if the pointer is over
1723 if (!tv || !tv->is_track()) {
1724 /* To make sure we hide the verbose canvas cursor when the mouse is
1725 not held over an audiotrack.
1727 _editor->verbose_cursor()->hide ();
1731 framepos_t where = adjusted_current_frame (event);
1732 assert (where >= 0);
1734 double delta = compute_x_delta (event, &after);
1736 framecnt_t amount = _editor->pixel_to_sample (delta);
1738 if (allow_moves_across_tracks) {
1739 // all the originally selected regions were on the same track
1741 framecnt_t adjust = 0;
1742 if (prev_tav && tv != prev_tav) {
1743 // dragged onto a different track
1744 // remove the unselected regions from _views, restore them to their original positions
1745 // and add the regions after the drop point on the new playlist to _views instead.
1746 // undo the effect of rippling the previous playlist, and include the effect of removing
1747 // the dragged region(s) from this track
1748 std::cerr << "dragged from " << prev_tav->name() << " to " << tv->name() << std::endl;
1750 remove_unselected_from_views (prev_amount, false);
1751 // ripple previous playlist according to the regions that have been removed onto the new playlist
1752 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
1755 // move just the selected regions
1756 RegionMoveDrag::motion(event, first_move);
1758 // ensure that the ripple operation on the new playlist inserts selection_length time
1759 adjust = selection_length;
1760 // ripple the new current playlist
1761 tv->playlist()->ripple (where, amount+adjust, exclude);
1763 // add regions after point where drag entered this track to subsequent ripples
1764 add_all_after_to_views (tv, where, _editor->selection->regions, true);
1767 // motion on same track
1768 RegionMoveDrag::motion(event, first_move);
1772 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
1773 prev_position = where;
1775 // selection encompasses multiple tracks - just drag
1776 // cross-track drags are forbidden
1777 RegionMoveDrag::motion(event, first_move);
1780 if (!_x_constrained) {
1781 prev_amount += amount;
1784 _last_frame_position = after;
1788 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
1790 if (!movement_occurred) {
1794 _editor->begin_reversible_command(_("Ripple drag"));
1796 // remove the regions being rippled from the dragging view, updating them to
1797 // their new positions
1798 remove_unselected_from_views (prev_amount, true);
1800 if (allow_moves_across_tracks) {
1802 // if regions were dragged across tracks, we've rippled any later
1803 // regions on the track the regions were dragged off, so we need
1804 // to add the original track to the undo record
1805 std::cerr << "adding orig_tav " << orig_tav->name() << " to undo" << std::endl;
1806 orig_tav->playlist()->clear_changes();
1807 vector<Command*> cmds;
1808 orig_tav->playlist()->rdiff (cmds);
1809 _editor->session()->add_commands (cmds);
1811 if (prev_tav && prev_tav != orig_tav) {
1812 std::cerr << "adding prev_tav " << prev_tav->name() << " to undo" << std::endl;
1813 prev_tav->playlist()->clear_changes();
1814 vector<Command*> cmds;
1815 prev_tav->playlist()->rdiff (cmds);
1816 _editor->session()->add_commands (cmds);
1817 } else if (prev_tav) {
1818 std::cerr << "prev_tav == orig_tav" << std::endl;
1821 // selection spanned multiple tracks - all will need adding to undo record
1823 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
1824 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1826 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1828 std::cerr << "adding playlist with selection " << (*pi)->name() << " to undo" << std::endl;
1829 (*pi)->clear_changes();
1830 vector<Command*> cmds;
1831 (*pi)->rdiff (cmds);
1832 _editor->session()->add_commands (cmds);
1838 // other modified playlists are added to undo by RegionMoveDrag::finished()
1839 RegionMoveDrag::finished (event, movement_occurred);
1840 _editor->commit_reversible_command();
1844 RegionRippleDrag::aborted (bool movement_occurred)
1846 RegionMoveDrag::aborted (movement_occurred);
1851 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1853 _view (dynamic_cast<MidiTimeAxisView*> (v))
1855 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1861 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1864 _region = add_midi_region (_view);
1865 _view->playlist()->freeze ();
1868 framepos_t const f = adjusted_current_frame (event);
1869 if (f < grab_frame()) {
1870 _region->set_position (f);
1873 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1874 so that if this region is duplicated, its duplicate starts on
1875 a snap point rather than 1 frame after a snap point. Otherwise things get
1876 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1877 place snapped notes at the start of the region.
1880 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1881 _region->set_length (len < 1 ? 1 : len);
1887 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1889 if (!movement_occurred) {
1890 add_midi_region (_view);
1892 _view->playlist()->thaw ();
1897 RegionCreateDrag::aborted (bool)
1900 _view->playlist()->thaw ();
1906 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1910 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1914 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1916 Gdk::Cursor* cursor;
1917 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1919 float x_fraction = cnote->mouse_x_fraction ();
1921 if (x_fraction > 0.0 && x_fraction < 0.25) {
1922 cursor = _editor->cursors()->left_side_trim;
1924 cursor = _editor->cursors()->right_side_trim;
1927 Drag::start_grab (event, cursor);
1929 region = &cnote->region_view();
1931 double const region_start = region->get_position_pixels();
1932 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1934 if (grab_x() <= middle_point) {
1935 cursor = _editor->cursors()->left_side_trim;
1938 cursor = _editor->cursors()->right_side_trim;
1944 if (event->motion.state & Keyboard::PrimaryModifier) {
1950 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1952 if (ms.size() > 1) {
1953 /* has to be relative, may make no sense otherwise */
1957 /* select this note; if it is already selected, preserve the existing selection,
1958 otherwise make this note the only one selected.
1960 region->note_selected (cnote, cnote->selected ());
1962 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1963 MidiRegionSelection::iterator next;
1966 (*r)->begin_resizing (at_front);
1972 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1974 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1975 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1976 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1978 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1983 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1985 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1986 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1987 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1989 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1994 NoteResizeDrag::aborted (bool)
1996 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1997 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1998 (*r)->abort_resizing ();
2002 AVDraggingView::AVDraggingView (RegionView* v)
2005 initial_position = v->region()->position ();
2008 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2011 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2014 TrackViewList empty;
2016 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2017 std::list<RegionView*> views = rs.by_layer();
2019 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2020 RegionView* rv = (*i);
2021 if (!rv->region()->video_locked()) {
2024 _views.push_back (AVDraggingView (rv));
2029 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2031 Drag::start_grab (event);
2032 if (_editor->session() == 0) {
2036 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2037 _max_backwards_drag = (
2038 ARDOUR_UI::instance()->video_timeline->get_duration()
2039 + ARDOUR_UI::instance()->video_timeline->get_offset()
2040 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2043 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2044 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2045 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2048 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2051 Timecode::Time timecode;
2052 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2053 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);
2054 show_verbose_cursor_text (buf);
2058 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2060 if (_editor->session() == 0) {
2063 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2067 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2068 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2070 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2071 dt = - _max_backwards_drag;
2074 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2075 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2077 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2078 RegionView* rv = i->view;
2079 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2082 rv->region()->clear_changes ();
2083 rv->region()->suspend_property_changes();
2085 rv->region()->set_position(i->initial_position + dt);
2086 rv->region_changed(ARDOUR::Properties::position);
2089 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2090 Timecode::Time timecode;
2091 Timecode::Time timediff;
2093 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2094 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2095 snprintf (buf, sizeof (buf),
2096 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2097 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2098 , _("Video Start:"),
2099 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2101 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2103 show_verbose_cursor_text (buf);
2107 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2109 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2113 if (!movement_occurred || ! _editor->session()) {
2117 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2119 _editor->begin_reversible_command (_("Move Video"));
2121 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2122 ARDOUR_UI::instance()->video_timeline->save_undo();
2123 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2124 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2126 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2127 i->view->drag_end();
2128 i->view->region()->resume_property_changes ();
2130 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2133 _editor->session()->maybe_update_session_range(
2134 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2135 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2139 _editor->commit_reversible_command ();
2143 VideoTimeLineDrag::aborted (bool)
2145 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2148 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2149 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2151 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2152 i->view->region()->resume_property_changes ();
2153 i->view->region()->set_position(i->initial_position);
2157 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2158 : RegionDrag (e, i, p, v)
2159 , _preserve_fade_anchor (preserve_fade_anchor)
2160 , _jump_position_when_done (false)
2162 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2166 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2169 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2170 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2172 if (tv && tv->is_track()) {
2173 speed = tv->track()->speed();
2176 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2177 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2178 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2180 framepos_t const pf = adjusted_current_frame (event);
2182 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2183 /* Move the contents of the region around without changing the region bounds */
2184 _operation = ContentsTrim;
2185 Drag::start_grab (event, _editor->cursors()->trimmer);
2187 /* These will get overridden for a point trim.*/
2188 if (pf < (region_start + region_length/2)) {
2189 /* closer to front */
2190 _operation = StartTrim;
2192 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2193 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2195 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2199 _operation = EndTrim;
2200 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2201 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2203 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2208 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2209 _jump_position_when_done = true;
2212 switch (_operation) {
2214 show_verbose_cursor_time (region_start);
2215 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2216 i->view->trim_front_starting ();
2220 show_verbose_cursor_time (region_end);
2223 show_verbose_cursor_time (pf);
2227 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2228 i->view->region()->suspend_property_changes ();
2233 TrimDrag::motion (GdkEvent* event, bool first_move)
2235 RegionView* rv = _primary;
2238 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2239 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2240 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2241 frameoffset_t frame_delta = 0;
2243 if (tv && tv->is_track()) {
2244 speed = tv->track()->speed();
2247 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2253 switch (_operation) {
2255 trim_type = "Region start trim";
2258 trim_type = "Region end trim";
2261 trim_type = "Region content trim";
2268 _editor->begin_reversible_command (trim_type);
2270 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2271 RegionView* rv = i->view;
2272 rv->enable_display (false);
2273 rv->region()->playlist()->clear_owned_changes ();
2275 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2278 arv->temporarily_hide_envelope ();
2282 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2283 insert_result = _editor->motion_frozen_playlists.insert (pl);
2285 if (insert_result.second) {
2291 bool non_overlap_trim = false;
2293 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2294 non_overlap_trim = true;
2297 /* contstrain trim to fade length */
2298 if (_preserve_fade_anchor) {
2299 switch (_operation) {
2301 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2302 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2304 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2305 if (ar->locked()) continue;
2306 framecnt_t len = ar->fade_in()->back()->when;
2307 if (len < dt) dt = min(dt, len);
2311 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2312 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2314 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2315 if (ar->locked()) continue;
2316 framecnt_t len = ar->fade_out()->back()->when;
2317 if (len < -dt) dt = max(dt, -len);
2326 switch (_operation) {
2328 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2329 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2330 if (changed && _preserve_fade_anchor) {
2331 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2333 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2334 framecnt_t len = ar->fade_in()->back()->when;
2335 framecnt_t diff = ar->first_frame() - i->initial_position;
2336 framepos_t new_length = len - diff;
2337 i->anchored_fade_length = min (ar->length(), new_length);
2338 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2339 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2346 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2347 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2348 if (changed && _preserve_fade_anchor) {
2349 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2351 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2352 framecnt_t len = ar->fade_out()->back()->when;
2353 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2354 framepos_t new_length = len + diff;
2355 i->anchored_fade_length = min (ar->length(), new_length);
2356 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2357 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2365 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2367 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2368 i->view->move_contents (frame_delta);
2374 switch (_operation) {
2376 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2379 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2382 // show_verbose_cursor_time (frame_delta);
2389 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2391 if (movement_occurred) {
2392 motion (event, false);
2394 if (_operation == StartTrim) {
2395 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2397 /* This must happen before the region's StatefulDiffCommand is created, as it may
2398 `correct' (ahem) the region's _start from being negative to being zero. It
2399 needs to be zero in the undo record.
2401 i->view->trim_front_ending ();
2403 if (_preserve_fade_anchor) {
2404 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2406 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2407 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2408 ar->set_fade_in_length(i->anchored_fade_length);
2409 ar->set_fade_in_active(true);
2412 if (_jump_position_when_done) {
2413 i->view->region()->set_position (i->initial_position);
2416 } else if (_operation == EndTrim) {
2417 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2418 if (_preserve_fade_anchor) {
2419 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2421 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2422 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2423 ar->set_fade_out_length(i->anchored_fade_length);
2424 ar->set_fade_out_active(true);
2427 if (_jump_position_when_done) {
2428 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2433 if (!_views.empty()) {
2434 if (_operation == StartTrim) {
2435 _editor->maybe_locate_with_edit_preroll(
2436 _views.begin()->view->region()->position());
2438 if (_operation == EndTrim) {
2439 _editor->maybe_locate_with_edit_preroll(
2440 _views.begin()->view->region()->position() +
2441 _views.begin()->view->region()->length());
2445 if (!_editor->selection->selected (_primary)) {
2446 _primary->thaw_after_trim ();
2449 set<boost::shared_ptr<Playlist> > diffed_playlists;
2451 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2452 i->view->thaw_after_trim ();
2453 i->view->enable_display (true);
2455 /* Trimming one region may affect others on the playlist, so we need
2456 to get undo Commands from the whole playlist rather than just the
2457 region. Use diffed_playlists to make sure we don't diff a given
2458 playlist more than once.
2460 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2461 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2462 vector<Command*> cmds;
2464 _editor->session()->add_commands (cmds);
2465 diffed_playlists.insert (p);
2470 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2474 _editor->motion_frozen_playlists.clear ();
2475 _editor->commit_reversible_command();
2478 /* no mouse movement */
2479 _editor->point_trim (event, adjusted_current_frame (event));
2482 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2483 if (_operation == StartTrim) {
2484 i->view->trim_front_ending ();
2487 i->view->region()->resume_property_changes ();
2492 TrimDrag::aborted (bool movement_occurred)
2494 /* Our motion method is changing model state, so use the Undo system
2495 to cancel. Perhaps not ideal, as this will leave an Undo point
2496 behind which may be slightly odd from the user's point of view.
2501 if (movement_occurred) {
2505 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2506 i->view->region()->resume_property_changes ();
2511 TrimDrag::setup_pointer_frame_offset ()
2513 list<DraggingView>::iterator i = _views.begin ();
2514 while (i != _views.end() && i->view != _primary) {
2518 if (i == _views.end()) {
2522 switch (_operation) {
2524 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2527 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2534 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2538 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2539 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2544 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2546 Drag::start_grab (event, cursor);
2547 show_verbose_cursor_time (adjusted_current_frame(event));
2551 MeterMarkerDrag::setup_pointer_frame_offset ()
2553 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2557 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2559 if (!_marker->meter().movable()) {
2565 // create a dummy marker for visual representation of moving the
2566 // section, because whether its a copy or not, we're going to
2567 // leave or lose the original marker (leave if its a copy; lose if its
2568 // not, because we'll remove it from the map).
2570 MeterSection section (_marker->meter());
2572 if (!section.movable()) {
2577 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2579 _marker = new MeterMarker (
2581 *_editor->meter_group,
2582 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2584 *new MeterSection (_marker->meter())
2587 /* use the new marker for the grab */
2588 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2591 TempoMap& map (_editor->session()->tempo_map());
2592 /* get current state */
2593 before_state = &map.get_state();
2594 /* remove the section while we drag it */
2595 map.remove_meter (section, true);
2599 framepos_t const pf = adjusted_current_frame (event);
2600 _marker->set_position (pf);
2601 show_verbose_cursor_time (pf);
2605 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2607 if (!movement_occurred) {
2608 if (was_double_click()) {
2609 _editor->edit_meter_marker (*_marker);
2614 if (!_marker->meter().movable()) {
2618 motion (event, false);
2620 Timecode::BBT_Time when;
2622 TempoMap& map (_editor->session()->tempo_map());
2623 map.bbt_time (last_pointer_frame(), when);
2625 if (_copy == true) {
2626 _editor->begin_reversible_command (_("copy meter mark"));
2627 XMLNode &before = map.get_state();
2628 map.add_meter (_marker->meter(), when);
2629 XMLNode &after = map.get_state();
2630 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2631 _editor->commit_reversible_command ();
2634 _editor->begin_reversible_command (_("move meter mark"));
2636 /* we removed it before, so add it back now */
2638 map.add_meter (_marker->meter(), when);
2639 XMLNode &after = map.get_state();
2640 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2641 _editor->commit_reversible_command ();
2644 // delete the dummy marker we used for visual representation while moving.
2645 // a new visual marker will show up automatically.
2650 MeterMarkerDrag::aborted (bool moved)
2652 _marker->set_position (_marker->meter().frame ());
2655 TempoMap& map (_editor->session()->tempo_map());
2656 /* we removed it before, so add it back now */
2657 map.add_meter (_marker->meter(), _marker->meter().frame());
2658 // delete the dummy marker we used for visual representation while moving.
2659 // a new visual marker will show up automatically.
2664 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2668 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2670 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2675 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2677 Drag::start_grab (event, cursor);
2678 show_verbose_cursor_time (adjusted_current_frame (event));
2682 TempoMarkerDrag::setup_pointer_frame_offset ()
2684 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2688 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2690 if (!_marker->tempo().movable()) {
2696 // create a dummy marker for visual representation of moving the
2697 // section, because whether its a copy or not, we're going to
2698 // leave or lose the original marker (leave if its a copy; lose if its
2699 // not, because we'll remove it from the map).
2701 // create a dummy marker for visual representation of moving the copy.
2702 // The actual copying is not done before we reach the finish callback.
2705 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2707 TempoSection section (_marker->tempo());
2709 _marker = new TempoMarker (
2711 *_editor->tempo_group,
2712 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2714 *new TempoSection (_marker->tempo())
2717 /* use the new marker for the grab */
2718 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2721 TempoMap& map (_editor->session()->tempo_map());
2722 /* get current state */
2723 before_state = &map.get_state();
2724 /* remove the section while we drag it */
2725 map.remove_tempo (section, true);
2729 framepos_t const pf = adjusted_current_frame (event);
2730 _marker->set_position (pf);
2731 show_verbose_cursor_time (pf);
2735 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2737 if (!movement_occurred) {
2738 if (was_double_click()) {
2739 _editor->edit_tempo_marker (*_marker);
2744 if (!_marker->tempo().movable()) {
2748 motion (event, false);
2750 TempoMap& map (_editor->session()->tempo_map());
2751 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2752 Timecode::BBT_Time when;
2754 map.bbt_time (beat_time, when);
2756 if (_copy == true) {
2757 _editor->begin_reversible_command (_("copy tempo mark"));
2758 XMLNode &before = map.get_state();
2759 map.add_tempo (_marker->tempo(), when);
2760 XMLNode &after = map.get_state();
2761 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2762 _editor->commit_reversible_command ();
2765 _editor->begin_reversible_command (_("move tempo mark"));
2766 /* we removed it before, so add it back now */
2767 map.add_tempo (_marker->tempo(), when);
2768 XMLNode &after = map.get_state();
2769 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2770 _editor->commit_reversible_command ();
2773 // delete the dummy marker we used for visual representation while moving.
2774 // a new visual marker will show up automatically.
2779 TempoMarkerDrag::aborted (bool moved)
2781 _marker->set_position (_marker->tempo().frame());
2783 TempoMap& map (_editor->session()->tempo_map());
2784 /* we removed it before, so add it back now */
2785 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2786 // delete the dummy marker we used for visual representation while moving.
2787 // a new visual marker will show up automatically.
2792 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2793 : Drag (e, &c.track_canvas_item(), false)
2797 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2800 /** Do all the things we do when dragging the playhead to make it look as though
2801 * we have located, without actually doing the locate (because that would cause
2802 * the diskstream buffers to be refilled, which is too slow).
2805 CursorDrag::fake_locate (framepos_t t)
2807 _editor->playhead_cursor->set_position (t);
2809 Session* s = _editor->session ();
2810 if (s->timecode_transmission_suspended ()) {
2811 framepos_t const f = _editor->playhead_cursor->current_frame ();
2812 /* This is asynchronous so it will be sent "now"
2814 s->send_mmc_locate (f);
2815 /* These are synchronous and will be sent during the next
2818 s->queue_full_time_code ();
2819 s->queue_song_position_pointer ();
2822 show_verbose_cursor_time (t);
2823 _editor->UpdateAllTransportClocks (t);
2827 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2829 Drag::start_grab (event, c);
2831 _grab_zoom = _editor->samples_per_pixel;
2833 framepos_t where = _editor->canvas_event_sample (event);
2835 _editor->snap_to_with_modifier (where, event);
2837 _editor->_dragging_playhead = true;
2839 Session* s = _editor->session ();
2841 /* grab the track canvas item as well */
2843 _cursor.track_canvas_item().grab();
2846 if (_was_rolling && _stop) {
2850 if (s->is_auditioning()) {
2851 s->cancel_audition ();
2855 if (AudioEngine::instance()->connected()) {
2857 /* do this only if we're the engine is connected
2858 * because otherwise this request will never be
2859 * serviced and we'll busy wait forever. likewise,
2860 * notice if we are disconnected while waiting for the
2861 * request to be serviced.
2864 s->request_suspend_timecode_transmission ();
2865 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2866 /* twiddle our thumbs */
2871 fake_locate (where);
2875 CursorDrag::motion (GdkEvent* event, bool)
2877 framepos_t const adjusted_frame = adjusted_current_frame (event);
2878 if (adjusted_frame != last_pointer_frame()) {
2879 fake_locate (adjusted_frame);
2884 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2886 _editor->_dragging_playhead = false;
2888 _cursor.track_canvas_item().ungrab();
2890 if (!movement_occurred && _stop) {
2894 motion (event, false);
2896 Session* s = _editor->session ();
2898 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2899 _editor->_pending_locate_request = true;
2900 s->request_resume_timecode_transmission ();
2905 CursorDrag::aborted (bool)
2907 _cursor.track_canvas_item().ungrab();
2909 if (_editor->_dragging_playhead) {
2910 _editor->session()->request_resume_timecode_transmission ();
2911 _editor->_dragging_playhead = false;
2914 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2917 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2918 : RegionDrag (e, i, p, v)
2920 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2924 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2926 Drag::start_grab (event, cursor);
2928 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2929 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2931 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2935 FadeInDrag::setup_pointer_frame_offset ()
2937 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2938 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2939 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2943 FadeInDrag::motion (GdkEvent* event, bool)
2945 framecnt_t fade_length;
2947 framepos_t const pos = adjusted_current_frame (event);
2949 boost::shared_ptr<Region> region = _primary->region ();
2951 if (pos < (region->position() + 64)) {
2952 fade_length = 64; // this should be a minimum defined somewhere
2953 } else if (pos > region->last_frame()) {
2954 fade_length = region->length();
2956 fade_length = pos - region->position();
2959 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2961 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2967 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2970 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2974 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2976 if (!movement_occurred) {
2980 framecnt_t fade_length;
2982 framepos_t const pos = adjusted_current_frame (event);
2984 boost::shared_ptr<Region> region = _primary->region ();
2986 if (pos < (region->position() + 64)) {
2987 fade_length = 64; // this should be a minimum defined somewhere
2988 } else if (pos > region->last_frame()) {
2989 fade_length = region->length();
2991 fade_length = pos - region->position();
2994 _editor->begin_reversible_command (_("change fade in length"));
2996 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2998 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3004 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3005 XMLNode &before = alist->get_state();
3007 tmp->audio_region()->set_fade_in_length (fade_length);
3008 tmp->audio_region()->set_fade_in_active (true);
3010 XMLNode &after = alist->get_state();
3011 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3014 _editor->commit_reversible_command ();
3018 FadeInDrag::aborted (bool)
3020 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3021 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3027 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3031 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3032 : RegionDrag (e, i, p, v)
3034 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3038 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3040 Drag::start_grab (event, cursor);
3042 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3043 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3045 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3049 FadeOutDrag::setup_pointer_frame_offset ()
3051 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3052 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3053 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3057 FadeOutDrag::motion (GdkEvent* event, bool)
3059 framecnt_t fade_length;
3061 framepos_t const pos = adjusted_current_frame (event);
3063 boost::shared_ptr<Region> region = _primary->region ();
3065 if (pos > (region->last_frame() - 64)) {
3066 fade_length = 64; // this should really be a minimum fade defined somewhere
3068 else if (pos < region->position()) {
3069 fade_length = region->length();
3072 fade_length = region->last_frame() - pos;
3075 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3077 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3083 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3086 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3090 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3092 if (!movement_occurred) {
3096 framecnt_t fade_length;
3098 framepos_t const pos = adjusted_current_frame (event);
3100 boost::shared_ptr<Region> region = _primary->region ();
3102 if (pos > (region->last_frame() - 64)) {
3103 fade_length = 64; // this should really be a minimum fade defined somewhere
3105 else if (pos < region->position()) {
3106 fade_length = region->length();
3109 fade_length = region->last_frame() - pos;
3112 _editor->begin_reversible_command (_("change fade out length"));
3114 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3116 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3122 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3123 XMLNode &before = alist->get_state();
3125 tmp->audio_region()->set_fade_out_length (fade_length);
3126 tmp->audio_region()->set_fade_out_active (true);
3128 XMLNode &after = alist->get_state();
3129 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3132 _editor->commit_reversible_command ();
3136 FadeOutDrag::aborted (bool)
3138 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3139 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3145 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3149 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3152 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3154 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3157 _points.push_back (ArdourCanvas::Duple (0, 0));
3158 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3161 MarkerDrag::~MarkerDrag ()
3163 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3168 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3170 location = new Location (*l);
3171 markers.push_back (m);
3176 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3178 Drag::start_grab (event, cursor);
3182 Location *location = _editor->find_location_from_marker (_marker, is_start);
3183 _editor->_dragging_edit_point = true;
3185 update_item (location);
3187 // _drag_line->show();
3188 // _line->raise_to_top();
3191 show_verbose_cursor_time (location->start());
3193 show_verbose_cursor_time (location->end());
3196 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3199 case Selection::Toggle:
3200 /* we toggle on the button release */
3202 case Selection::Set:
3203 if (!_editor->selection->selected (_marker)) {
3204 _editor->selection->set (_marker);
3207 case Selection::Extend:
3209 Locations::LocationList ll;
3210 list<Marker*> to_add;
3212 _editor->selection->markers.range (s, e);
3213 s = min (_marker->position(), s);
3214 e = max (_marker->position(), e);
3217 if (e < max_framepos) {
3220 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3221 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3222 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3225 to_add.push_back (lm->start);
3228 to_add.push_back (lm->end);
3232 if (!to_add.empty()) {
3233 _editor->selection->add (to_add);
3237 case Selection::Add:
3238 _editor->selection->add (_marker);
3242 /* Set up copies for us to manipulate during the drag
3245 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3247 Location* l = _editor->find_location_from_marker (*i, is_start);
3254 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3256 /* range: check that the other end of the range isn't
3259 CopiedLocationInfo::iterator x;
3260 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3261 if (*(*x).location == *l) {
3265 if (x == _copied_locations.end()) {
3266 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3268 (*x).markers.push_back (*i);
3269 (*x).move_both = true;
3277 MarkerDrag::setup_pointer_frame_offset ()
3280 Location *location = _editor->find_location_from_marker (_marker, is_start);
3281 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3285 MarkerDrag::motion (GdkEvent* event, bool)
3287 framecnt_t f_delta = 0;
3289 bool move_both = false;
3290 Location *real_location;
3291 Location *copy_location = 0;
3293 framepos_t const newframe = adjusted_current_frame (event);
3294 framepos_t next = newframe;
3296 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3300 CopiedLocationInfo::iterator x;
3302 /* find the marker we're dragging, and compute the delta */
3304 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3306 copy_location = (*x).location;
3308 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3310 /* this marker is represented by this
3311 * CopiedLocationMarkerInfo
3314 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3319 if (real_location->is_mark()) {
3320 f_delta = newframe - copy_location->start();
3324 switch (_marker->type()) {
3325 case Marker::SessionStart:
3326 case Marker::RangeStart:
3327 case Marker::LoopStart:
3328 case Marker::PunchIn:
3329 f_delta = newframe - copy_location->start();
3332 case Marker::SessionEnd:
3333 case Marker::RangeEnd:
3334 case Marker::LoopEnd:
3335 case Marker::PunchOut:
3336 f_delta = newframe - copy_location->end();
3339 /* what kind of marker is this ? */
3348 if (x == _copied_locations.end()) {
3349 /* hmm, impossible - we didn't find the dragged marker */
3353 /* now move them all */
3355 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3357 copy_location = x->location;
3359 /* call this to find out if its the start or end */
3361 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3365 if (real_location->locked()) {
3369 if (copy_location->is_mark()) {
3373 copy_location->set_start (copy_location->start() + f_delta);
3377 framepos_t new_start = copy_location->start() + f_delta;
3378 framepos_t new_end = copy_location->end() + f_delta;
3380 if (is_start) { // start-of-range marker
3382 if (move_both || (*x).move_both) {
3383 copy_location->set_start (new_start);
3384 copy_location->set_end (new_end);
3385 } else if (new_start < copy_location->end()) {
3386 copy_location->set_start (new_start);
3387 } else if (newframe > 0) {
3388 _editor->snap_to (next, 1, true);
3389 copy_location->set_end (next);
3390 copy_location->set_start (newframe);
3393 } else { // end marker
3395 if (move_both || (*x).move_both) {
3396 copy_location->set_end (new_end);
3397 copy_location->set_start (new_start);
3398 } else if (new_end > copy_location->start()) {
3399 copy_location->set_end (new_end);
3400 } else if (newframe > 0) {
3401 _editor->snap_to (next, -1, true);
3402 copy_location->set_start (next);
3403 copy_location->set_end (newframe);
3408 update_item (copy_location);
3410 /* now lookup the actual GUI items used to display this
3411 * location and move them to wherever the copy of the location
3412 * is now. This means that the logic in ARDOUR::Location is
3413 * still enforced, even though we are not (yet) modifying
3414 * the real Location itself.
3417 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3420 lm->set_position (copy_location->start(), copy_location->end());
3425 assert (!_copied_locations.empty());
3427 show_verbose_cursor_time (newframe);
3431 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3433 if (!movement_occurred) {
3435 if (was_double_click()) {
3436 _editor->rename_marker (_marker);
3440 /* just a click, do nothing but finish
3441 off the selection process
3444 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3447 case Selection::Set:
3448 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3449 _editor->selection->set (_marker);
3453 case Selection::Toggle:
3454 /* we toggle on the button release, click only */
3455 _editor->selection->toggle (_marker);
3458 case Selection::Extend:
3459 case Selection::Add:
3466 _editor->_dragging_edit_point = false;
3468 _editor->begin_reversible_command ( _("move marker") );
3469 XMLNode &before = _editor->session()->locations()->get_state();
3471 MarkerSelection::iterator i;
3472 CopiedLocationInfo::iterator x;
3475 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3476 x != _copied_locations.end() && i != _editor->selection->markers.end();
3479 Location * location = _editor->find_location_from_marker (*i, is_start);
3483 if (location->locked()) {
3487 if (location->is_mark()) {
3488 location->set_start (((*x).location)->start());
3490 location->set (((*x).location)->start(), ((*x).location)->end());
3495 XMLNode &after = _editor->session()->locations()->get_state();
3496 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3497 _editor->commit_reversible_command ();
3501 MarkerDrag::aborted (bool)
3507 MarkerDrag::update_item (Location*)
3512 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3514 _cumulative_x_drag (0),
3515 _cumulative_y_drag (0)
3517 if (_zero_gain_fraction < 0.0) {
3518 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3521 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3523 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3529 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3531 Drag::start_grab (event, _editor->cursors()->fader);
3533 // start the grab at the center of the control point so
3534 // the point doesn't 'jump' to the mouse after the first drag
3535 _fixed_grab_x = _point->get_x();
3536 _fixed_grab_y = _point->get_y();
3538 float const fraction = 1 - (_point->get_y() / _point->line().height());
3540 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3542 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3544 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3546 if (!_point->can_slide ()) {
3547 _x_constrained = true;
3552 ControlPointDrag::motion (GdkEvent* event, bool)
3554 double dx = _drags->current_pointer_x() - last_pointer_x();
3555 double dy = current_pointer_y() - last_pointer_y();
3557 if (event->button.state & Keyboard::SecondaryModifier) {
3562 /* coordinate in pixels relative to the start of the region (for region-based automation)
3563 or track (for track-based automation) */
3564 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3565 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3567 // calculate zero crossing point. back off by .01 to stay on the
3568 // positive side of zero
3569 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3571 // make sure we hit zero when passing through
3572 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3576 if (_x_constrained) {
3579 if (_y_constrained) {
3583 _cumulative_x_drag = cx - _fixed_grab_x;
3584 _cumulative_y_drag = cy - _fixed_grab_y;
3588 cy = min ((double) _point->line().height(), cy);
3590 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3592 if (!_x_constrained) {
3593 _editor->snap_to_with_modifier (cx_frames, event);
3596 cx_frames = min (cx_frames, _point->line().maximum_time());
3598 float const fraction = 1.0 - (cy / _point->line().height());
3600 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3602 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3606 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3608 if (!movement_occurred) {
3612 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3613 _editor->reset_point_selection ();
3617 motion (event, false);
3620 _point->line().end_drag (_pushing, _final_index);
3621 _editor->session()->commit_reversible_command ();
3625 ControlPointDrag::aborted (bool)
3627 _point->line().reset ();
3631 ControlPointDrag::active (Editing::MouseMode m)
3633 if (m == Editing::MouseGain) {
3634 /* always active in mouse gain */
3638 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3639 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3642 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3645 _cumulative_y_drag (0)
3647 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3651 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3653 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3656 _item = &_line->grab_item ();
3658 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3659 origin, and ditto for y.
3662 double cx = event->button.x;
3663 double cy = event->button.y;
3665 _line->parent_group().canvas_to_item (cx, cy);
3667 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3672 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3673 /* no adjacent points */
3677 Drag::start_grab (event, _editor->cursors()->fader);
3679 /* store grab start in parent frame */
3684 double fraction = 1.0 - (cy / _line->height());
3686 _line->start_drag_line (before, after, fraction);
3688 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3692 LineDrag::motion (GdkEvent* event, bool)
3694 double dy = current_pointer_y() - last_pointer_y();
3696 if (event->button.state & Keyboard::SecondaryModifier) {
3700 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3702 _cumulative_y_drag = cy - _fixed_grab_y;
3705 cy = min ((double) _line->height(), cy);
3707 double const fraction = 1.0 - (cy / _line->height());
3710 /* we are ignoring x position for this drag, so we can just pass in anything */
3711 _line->drag_motion (0, fraction, true, false, ignored);
3713 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3717 LineDrag::finished (GdkEvent* event, bool movement_occured)
3719 if (movement_occured) {
3720 motion (event, false);
3721 _line->end_drag (false, 0);
3723 /* add a new control point on the line */
3725 AutomationTimeAxisView* atv;
3727 _line->end_drag (false, 0);
3729 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3730 framepos_t where = _editor->window_event_sample (event, 0, 0);
3731 atv->add_automation_event (event, where, event->button.y, false);
3735 _editor->session()->commit_reversible_command ();
3739 LineDrag::aborted (bool)
3744 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3747 _cumulative_x_drag (0)
3749 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3753 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3755 Drag::start_grab (event);
3757 _line = reinterpret_cast<Line*> (_item);
3760 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3762 double cx = event->button.x;
3763 double cy = event->button.y;
3765 _item->parent()->canvas_to_item (cx, cy);
3767 /* store grab start in parent frame */
3768 _region_view_grab_x = cx;
3770 _before = *(float*) _item->get_data ("position");
3772 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3774 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3778 FeatureLineDrag::motion (GdkEvent*, bool)
3780 double dx = _drags->current_pointer_x() - last_pointer_x();
3782 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3784 _cumulative_x_drag += dx;
3786 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3795 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3797 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3799 float *pos = new float;
3802 _line->set_data ("position", pos);
3808 FeatureLineDrag::finished (GdkEvent*, bool)
3810 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3811 _arv->update_transient(_before, _before);
3815 FeatureLineDrag::aborted (bool)
3820 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3822 , _vertical_only (false)
3824 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3828 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3830 Drag::start_grab (event);
3831 show_verbose_cursor_time (adjusted_current_frame (event));
3835 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3842 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3844 framepos_t grab = grab_frame ();
3845 if (Config->get_rubberbanding_snaps_to_grid ()) {
3846 _editor->snap_to_with_modifier (grab, event);
3849 /* base start and end on initial click position */
3859 if (current_pointer_y() < grab_y()) {
3860 y1 = current_pointer_y();
3863 y2 = current_pointer_y();
3867 if (start != end || y1 != y2) {
3869 double x1 = _editor->sample_to_pixel (start);
3870 double x2 = _editor->sample_to_pixel (end);
3871 const double min_dimension = 2.0;
3873 if (_vertical_only) {
3874 /* fixed 10 pixel width */
3878 x2 = min (x1 - min_dimension, x2);
3880 x2 = max (x1 + min_dimension, x2);
3885 y2 = min (y1 - min_dimension, y2);
3887 y2 = max (y1 + min_dimension, y2);
3890 /* translate rect into item space and set */
3892 ArdourCanvas::Rect r (x1, y1, x2, y2);
3894 /* this drag is a _trackview_only == true drag, so the y1 and
3895 * y2 (computed using current_pointer_y() and grab_y()) will be
3896 * relative to the top of the trackview group). The
3897 * rubberband rect has the same parent/scroll offset as the
3898 * the trackview group, so we can use the "r" rect directly
3899 * to set the shape of the rubberband.
3902 _editor->rubberband_rect->set (r);
3903 _editor->rubberband_rect->show();
3904 _editor->rubberband_rect->raise_to_top();
3906 show_verbose_cursor_time (pf);
3908 do_select_things (event, true);
3913 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3918 if (grab_frame() < last_pointer_frame()) {
3920 x2 = last_pointer_frame ();
3923 x1 = last_pointer_frame ();
3929 if (current_pointer_y() < grab_y()) {
3930 y1 = current_pointer_y();
3933 y2 = current_pointer_y();
3937 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3941 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3943 if (movement_occurred) {
3945 motion (event, false);
3946 do_select_things (event, false);
3952 bool do_deselect = true;
3953 MidiTimeAxisView* mtv;
3955 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3957 if (_editor->selection->empty()) {
3958 /* nothing selected */
3959 add_midi_region (mtv);
3960 do_deselect = false;
3964 /* do not deselect if Primary or Tertiary (toggle-select or
3965 * extend-select are pressed.
3968 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3969 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3976 _editor->rubberband_rect->hide();
3980 RubberbandSelectDrag::aborted (bool)
3982 _editor->rubberband_rect->hide ();
3985 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3986 : RegionDrag (e, i, p, v)
3988 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3992 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3994 Drag::start_grab (event, cursor);
3996 show_verbose_cursor_time (adjusted_current_frame (event));
4000 TimeFXDrag::motion (GdkEvent* event, bool)
4002 RegionView* rv = _primary;
4003 StreamView* cv = rv->get_time_axis_view().view ();
4005 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4006 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4007 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4009 framepos_t const pf = adjusted_current_frame (event);
4011 if (pf > rv->region()->position()) {
4012 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4015 show_verbose_cursor_time (pf);
4019 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4021 _primary->get_time_axis_view().hide_timestretch ();
4023 if (!movement_occurred) {
4027 if (last_pointer_frame() < _primary->region()->position()) {
4028 /* backwards drag of the left edge - not usable */
4032 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4034 float percentage = (double) newlen / (double) _primary->region()->length();
4036 #ifndef USE_RUBBERBAND
4037 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4038 if (_primary->region()->data_type() == DataType::AUDIO) {
4039 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4043 if (!_editor->get_selection().regions.empty()) {
4044 /* primary will already be included in the selection, and edit
4045 group shared editing will propagate selection across
4046 equivalent regions, so just use the current region
4050 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4051 error << _("An error occurred while executing time stretch operation") << endmsg;
4057 TimeFXDrag::aborted (bool)
4059 _primary->get_time_axis_view().hide_timestretch ();
4062 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4065 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4069 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4071 Drag::start_grab (event);
4075 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4077 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4081 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4083 if (movement_occurred && _editor->session()) {
4084 /* make sure we stop */
4085 _editor->session()->request_transport_speed (0.0);
4090 ScrubDrag::aborted (bool)
4095 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4100 , _original_pointer_time_axis (-1)
4101 , _last_pointer_time_axis (-1)
4102 , _time_selection_at_start (!_editor->get_selection().time.empty())
4104 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4106 if (_time_selection_at_start) {
4107 start_at_start = _editor->get_selection().time.start();
4108 end_at_start = _editor->get_selection().time.end_frame();
4113 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4115 if (_editor->session() == 0) {
4119 Gdk::Cursor* cursor = 0;
4121 switch (_operation) {
4122 case CreateSelection:
4123 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4128 cursor = _editor->cursors()->selector;
4129 Drag::start_grab (event, cursor);
4132 case SelectionStartTrim:
4133 if (_editor->clicked_axisview) {
4134 _editor->clicked_axisview->order_selection_trims (_item, true);
4136 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4139 case SelectionEndTrim:
4140 if (_editor->clicked_axisview) {
4141 _editor->clicked_axisview->order_selection_trims (_item, false);
4143 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4147 Drag::start_grab (event, cursor);
4150 case SelectionExtend:
4151 Drag::start_grab (event, cursor);
4155 if (_operation == SelectionMove) {
4156 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4158 show_verbose_cursor_time (adjusted_current_frame (event));
4161 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4165 SelectionDrag::setup_pointer_frame_offset ()
4167 switch (_operation) {
4168 case CreateSelection:
4169 _pointer_frame_offset = 0;
4172 case SelectionStartTrim:
4174 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4177 case SelectionEndTrim:
4178 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4181 case SelectionExtend:
4187 SelectionDrag::motion (GdkEvent* event, bool first_move)
4189 framepos_t start = 0;
4191 framecnt_t length = 0;
4192 framecnt_t distance = 0;
4194 framepos_t const pending_position = adjusted_current_frame (event);
4196 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4200 switch (_operation) {
4201 case CreateSelection:
4203 framepos_t grab = grab_frame ();
4206 grab = adjusted_current_frame (event, false);
4207 if (grab < pending_position) {
4208 _editor->snap_to (grab, -1);
4210 _editor->snap_to (grab, 1);
4214 if (pending_position < grab) {
4215 start = pending_position;
4218 end = pending_position;
4222 /* first drag: Either add to the selection
4223 or create a new selection
4230 /* adding to the selection */
4231 _editor->set_selected_track_as_side_effect (Selection::Add);
4232 _editor->clicked_selection = _editor->selection->add (start, end);
4239 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4240 _editor->set_selected_track_as_side_effect (Selection::Set);
4243 _editor->clicked_selection = _editor->selection->set (start, end);
4247 /* select all tracks within the rectangle that we've marked out so far */
4248 TrackViewList to_be_added_to_selection;
4249 TrackViewList to_be_removed_from_selection;
4250 TrackViewList& all_tracks (_editor->track_views);
4252 ArdourCanvas::Coord const top = grab_y();
4253 ArdourCanvas::Coord const bottom = current_pointer_y();
4255 if (top >= 0 && bottom >= 0) {
4257 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4259 if ((*i)->covered_by_y_range (top, bottom)) {
4260 if (!(*i)->get_selected()) {
4261 to_be_added_to_selection.push_back (*i);
4264 if ((*i)->get_selected()) {
4265 to_be_removed_from_selection.push_back (*i);
4270 if (!to_be_added_to_selection.empty()) {
4271 _editor->selection->add (to_be_added_to_selection);
4274 if (!to_be_removed_from_selection.empty()) {
4275 _editor->selection->remove (to_be_removed_from_selection);
4281 case SelectionStartTrim:
4283 start = _editor->selection->time[_editor->clicked_selection].start;
4284 end = _editor->selection->time[_editor->clicked_selection].end;
4286 if (pending_position > end) {
4289 start = pending_position;
4293 case SelectionEndTrim:
4295 start = _editor->selection->time[_editor->clicked_selection].start;
4296 end = _editor->selection->time[_editor->clicked_selection].end;
4298 if (pending_position < start) {
4301 end = pending_position;
4308 start = _editor->selection->time[_editor->clicked_selection].start;
4309 end = _editor->selection->time[_editor->clicked_selection].end;
4311 length = end - start;
4312 distance = pending_position - start;
4313 start = pending_position;
4314 _editor->snap_to (start);
4316 end = start + length;
4320 case SelectionExtend:
4325 switch (_operation) {
4327 if (_time_selection_at_start) {
4328 _editor->selection->move_time (distance);
4332 _editor->selection->replace (_editor->clicked_selection, start, end);
4336 if (_operation == SelectionMove) {
4337 show_verbose_cursor_time(start);
4339 show_verbose_cursor_time(pending_position);
4344 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4346 Session* s = _editor->session();
4348 if (movement_occurred) {
4349 motion (event, false);
4350 /* XXX this is not object-oriented programming at all. ick */
4351 if (_editor->selection->time.consolidate()) {
4352 _editor->selection->TimeChanged ();
4355 /* XXX what if its a music time selection? */
4357 if ( s->get_play_range() && s->transport_rolling() ) {
4358 s->request_play_range (&_editor->selection->time, true);
4360 if (Config->get_always_play_range() && !s->transport_rolling()) {
4361 s->request_locate (_editor->get_selection().time.start());
4367 /* just a click, no pointer movement.
4370 if (_operation == SelectionExtend) {
4371 if (_time_selection_at_start) {
4372 framepos_t pos = adjusted_current_frame (event, false);
4373 framepos_t start = min (pos, start_at_start);
4374 framepos_t end = max (pos, end_at_start);
4375 _editor->selection->set (start, end);
4378 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4379 if (_editor->clicked_selection) {
4380 _editor->selection->remove (_editor->clicked_selection);
4383 if (!_editor->clicked_selection) {
4384 _editor->selection->clear_time();
4389 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4390 _editor->selection->set (_editor->clicked_axisview);
4393 if (s && s->get_play_range () && s->transport_rolling()) {
4394 s->request_stop (false, false);
4399 _editor->stop_canvas_autoscroll ();
4400 _editor->clicked_selection = 0;
4404 SelectionDrag::aborted (bool)
4409 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4410 : Drag (e, i, false),
4414 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4416 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4417 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4418 physical_screen_height (_editor->get_window())));
4419 _drag_rect->hide ();
4421 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4422 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4426 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4428 if (_editor->session() == 0) {
4432 Gdk::Cursor* cursor = 0;
4434 if (!_editor->temp_location) {
4435 _editor->temp_location = new Location (*_editor->session());
4438 switch (_operation) {
4439 case CreateRangeMarker:
4440 case CreateTransportMarker:
4441 case CreateCDMarker:
4443 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4448 cursor = _editor->cursors()->selector;
4452 Drag::start_grab (event, cursor);
4454 show_verbose_cursor_time (adjusted_current_frame (event));
4458 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4460 framepos_t start = 0;
4462 ArdourCanvas::Rectangle *crect;
4464 switch (_operation) {
4465 case CreateRangeMarker:
4466 crect = _editor->range_bar_drag_rect;
4468 case CreateTransportMarker:
4469 crect = _editor->transport_bar_drag_rect;
4471 case CreateCDMarker:
4472 crect = _editor->cd_marker_bar_drag_rect;
4475 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4480 framepos_t const pf = adjusted_current_frame (event);
4482 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4483 framepos_t grab = grab_frame ();
4484 _editor->snap_to (grab);
4486 if (pf < grab_frame()) {
4494 /* first drag: Either add to the selection
4495 or create a new selection.
4500 _editor->temp_location->set (start, end);
4504 update_item (_editor->temp_location);
4506 //_drag_rect->raise_to_top();
4512 _editor->temp_location->set (start, end);
4514 double x1 = _editor->sample_to_pixel (start);
4515 double x2 = _editor->sample_to_pixel (end);
4519 update_item (_editor->temp_location);
4522 show_verbose_cursor_time (pf);
4527 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4529 Location * newloc = 0;
4533 if (movement_occurred) {
4534 motion (event, false);
4537 switch (_operation) {
4538 case CreateRangeMarker:
4539 case CreateCDMarker:
4541 _editor->begin_reversible_command (_("new range marker"));
4542 XMLNode &before = _editor->session()->locations()->get_state();
4543 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4544 if (_operation == CreateCDMarker) {
4545 flags = Location::IsRangeMarker | Location::IsCDMarker;
4546 _editor->cd_marker_bar_drag_rect->hide();
4549 flags = Location::IsRangeMarker;
4550 _editor->range_bar_drag_rect->hide();
4552 newloc = new Location (
4553 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4556 _editor->session()->locations()->add (newloc, true);
4557 XMLNode &after = _editor->session()->locations()->get_state();
4558 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4559 _editor->commit_reversible_command ();
4563 case CreateTransportMarker:
4564 // popup menu to pick loop or punch
4565 _editor->new_transport_marker_context_menu (&event->button, _item);
4571 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4573 if (_operation == CreateTransportMarker) {
4575 /* didn't drag, so just locate */
4577 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4579 } else if (_operation == CreateCDMarker) {
4581 /* didn't drag, but mark is already created so do
4584 } else { /* operation == CreateRangeMarker */
4590 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4592 if (end == max_framepos) {
4593 end = _editor->session()->current_end_frame ();
4596 if (start == max_framepos) {
4597 start = _editor->session()->current_start_frame ();
4600 switch (_editor->mouse_mode) {
4602 /* find the two markers on either side and then make the selection from it */
4603 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4607 /* find the two markers on either side of the click and make the range out of it */
4608 _editor->selection->set (start, end);
4617 _editor->stop_canvas_autoscroll ();
4621 RangeMarkerBarDrag::aborted (bool)
4627 RangeMarkerBarDrag::update_item (Location* location)
4629 double const x1 = _editor->sample_to_pixel (location->start());
4630 double const x2 = _editor->sample_to_pixel (location->end());
4632 _drag_rect->set_x0 (x1);
4633 _drag_rect->set_x1 (x2);
4636 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4640 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4644 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4646 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4647 Drag::start_grab (event, _editor->cursors()->zoom_out);
4650 Drag::start_grab (event, _editor->cursors()->zoom_in);
4654 show_verbose_cursor_time (adjusted_current_frame (event));
4658 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4663 framepos_t const pf = adjusted_current_frame (event);
4665 framepos_t grab = grab_frame ();
4666 _editor->snap_to_with_modifier (grab, event);
4668 /* base start and end on initial click position */
4680 _editor->zoom_rect->show();
4681 _editor->zoom_rect->raise_to_top();
4684 _editor->reposition_zoom_rect(start, end);
4686 show_verbose_cursor_time (pf);
4691 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4693 if (movement_occurred) {
4694 motion (event, false);
4696 if (grab_frame() < last_pointer_frame()) {
4697 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4699 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4702 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4703 _editor->tav_zoom_step (_zoom_out);
4705 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4709 _editor->zoom_rect->hide();
4713 MouseZoomDrag::aborted (bool)
4715 _editor->zoom_rect->hide ();
4718 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4720 , _cumulative_dx (0)
4721 , _cumulative_dy (0)
4723 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4725 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4727 _region = &_primary->region_view ();
4728 _note_height = _region->midi_stream_view()->note_height ();
4732 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4734 Drag::start_grab (event);
4736 if (!(_was_selected = _primary->selected())) {
4738 /* tertiary-click means extend selection - we'll do that on button release,
4739 so don't add it here, because otherwise we make it hard to figure
4740 out the "extend-to" range.
4743 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4746 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4749 _region->note_selected (_primary, true);
4751 _region->unique_select (_primary);
4757 /** @return Current total drag x change in frames */
4759 NoteDrag::total_dx () const
4762 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4764 /* primary note time */
4765 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4767 /* new time of the primary note in session frames */
4768 frameoffset_t st = n + dx;
4770 framepos_t const rp = _region->region()->position ();
4772 /* prevent the note being dragged earlier than the region's position */
4775 /* snap and return corresponding delta */
4776 return _region->snap_frame_to_frame (st - rp) + rp - n;
4779 /** @return Current total drag y change in note number */
4781 NoteDrag::total_dy () const
4783 MidiStreamView* msv = _region->midi_stream_view ();
4784 double const y = _region->midi_view()->y_position ();
4785 /* new current note */
4786 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4788 n = max (msv->lowest_note(), n);
4789 n = min (msv->highest_note(), n);
4790 /* and work out delta */
4791 return n - msv->y_to_note (grab_y() - y);
4795 NoteDrag::motion (GdkEvent *, bool)
4797 /* Total change in x and y since the start of the drag */
4798 frameoffset_t const dx = total_dx ();
4799 int8_t const dy = total_dy ();
4801 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4802 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4803 double const tdy = -dy * _note_height - _cumulative_dy;
4806 _cumulative_dx += tdx;
4807 _cumulative_dy += tdy;
4809 int8_t note_delta = total_dy();
4811 _region->move_selection (tdx, tdy, note_delta);
4813 /* the new note value may be the same as the old one, but we
4814 * don't know what that means because the selection may have
4815 * involved more than one note and we might be doing something
4816 * odd with them. so show the note value anyway, always.
4820 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4822 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4823 (int) floor ((double)new_note));
4825 show_verbose_cursor_text (buf);
4830 NoteDrag::finished (GdkEvent* ev, bool moved)
4833 /* no motion - select note */
4835 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4836 _editor->current_mouse_mode() == Editing::MouseDraw) {
4838 if (_was_selected) {
4839 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4841 _region->note_deselected (_primary);
4844 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4845 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4847 if (!extend && !add && _region->selection_size() > 1) {
4848 _region->unique_select (_primary);
4849 } else if (extend) {
4850 _region->note_selected (_primary, true, true);
4852 /* it was added during button press */
4857 _region->note_dropped (_primary, total_dx(), total_dy());
4862 NoteDrag::aborted (bool)
4867 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4868 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4869 : Drag (editor, atv->base_item ())
4871 , _nothing_to_drag (false)
4873 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4874 y_origin = atv->y_position();
4875 setup (atv->lines ());
4878 /** Make an AutomationRangeDrag for region gain lines */
4879 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4880 : Drag (editor, rv->get_canvas_group ())
4882 , _nothing_to_drag (false)
4884 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4886 list<boost::shared_ptr<AutomationLine> > lines;
4887 lines.push_back (rv->get_gain_line ());
4888 y_origin = rv->get_time_axis_view().y_position();
4892 /** @param lines AutomationLines to drag.
4893 * @param offset Offset from the session start to the points in the AutomationLines.
4896 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4898 /* find the lines that overlap the ranges being dragged */
4899 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4900 while (i != lines.end ()) {
4901 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4904 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4906 /* check this range against all the AudioRanges that we are using */
4907 list<AudioRange>::const_iterator k = _ranges.begin ();
4908 while (k != _ranges.end()) {
4909 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4915 /* add it to our list if it overlaps at all */
4916 if (k != _ranges.end()) {
4921 _lines.push_back (n);
4927 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4931 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4933 return 1.0 - ((global_y - y_origin) / line->height());
4937 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4939 Drag::start_grab (event, cursor);
4941 /* Get line states before we start changing things */
4942 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4943 i->state = &i->line->get_state ();
4944 i->original_fraction = y_fraction (i->line, current_pointer_y());
4947 if (_ranges.empty()) {
4949 /* No selected time ranges: drag all points */
4950 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4951 uint32_t const N = i->line->npoints ();
4952 for (uint32_t j = 0; j < N; ++j) {
4953 i->points.push_back (i->line->nth (j));
4959 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4961 framecnt_t const half = (i->start + i->end) / 2;
4963 /* find the line that this audio range starts in */
4964 list<Line>::iterator j = _lines.begin();
4965 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4969 if (j != _lines.end()) {
4970 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4972 /* j is the line that this audio range starts in; fade into it;
4973 64 samples length plucked out of thin air.
4976 framepos_t a = i->start + 64;
4981 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4982 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4984 the_list->add (p, the_list->eval (p));
4985 the_list->add (q, the_list->eval (q));
4988 /* same thing for the end */
4991 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4995 if (j != _lines.end()) {
4996 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4998 /* j is the line that this audio range starts in; fade out of it;
4999 64 samples length plucked out of thin air.
5002 framepos_t b = i->end - 64;
5007 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5008 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5010 the_list->add (p, the_list->eval (p));
5011 the_list->add (q, the_list->eval (q));
5015 _nothing_to_drag = true;
5017 /* Find all the points that should be dragged and put them in the relevant
5018 points lists in the Line structs.
5021 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5023 uint32_t const N = i->line->npoints ();
5024 for (uint32_t j = 0; j < N; ++j) {
5026 /* here's a control point on this line */
5027 ControlPoint* p = i->line->nth (j);
5028 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5030 /* see if it's inside a range */
5031 list<AudioRange>::const_iterator k = _ranges.begin ();
5032 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5036 if (k != _ranges.end()) {
5037 /* dragging this point */
5038 _nothing_to_drag = false;
5039 i->points.push_back (p);
5045 if (_nothing_to_drag) {
5049 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5050 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5055 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5057 if (_nothing_to_drag) {
5061 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5062 float const f = y_fraction (l->line, current_pointer_y());
5063 /* we are ignoring x position for this drag, so we can just pass in anything */
5065 l->line->drag_motion (0, f, true, false, ignored);
5066 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5071 AutomationRangeDrag::finished (GdkEvent* event, bool)
5073 if (_nothing_to_drag) {
5077 motion (event, false);
5078 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5079 i->line->end_drag (false, 0);
5082 _editor->session()->commit_reversible_command ();
5086 AutomationRangeDrag::aborted (bool)
5088 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5093 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5095 , initial_time_axis_view (itav)
5097 /* note that time_axis_view may be null if the regionview was created
5098 * as part of a copy operation.
5100 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5101 layer = v->region()->layer ();
5102 initial_y = v->get_canvas_group()->position().y;
5103 initial_playlist = v->region()->playlist ();
5104 initial_position = v->region()->position ();
5105 initial_end = v->region()->position () + v->region()->length ();
5108 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5109 : Drag (e, i->canvas_item ())
5112 , _cumulative_dx (0)
5114 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5115 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5120 PatchChangeDrag::motion (GdkEvent* ev, bool)
5122 framepos_t f = adjusted_current_frame (ev);
5123 boost::shared_ptr<Region> r = _region_view->region ();
5124 f = max (f, r->position ());
5125 f = min (f, r->last_frame ());
5127 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5128 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5129 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5130 _cumulative_dx = dxu;
5134 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5136 if (!movement_occurred) {
5140 boost::shared_ptr<Region> r (_region_view->region ());
5141 framepos_t f = adjusted_current_frame (ev);
5142 f = max (f, r->position ());
5143 f = min (f, r->last_frame ());
5145 _region_view->move_patch_change (
5147 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5152 PatchChangeDrag::aborted (bool)
5154 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5158 PatchChangeDrag::setup_pointer_frame_offset ()
5160 boost::shared_ptr<Region> region = _region_view->region ();
5161 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5164 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5165 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5172 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5174 framepos_t const p = _region_view->region()->position ();
5175 double const y = _region_view->midi_view()->y_position ();
5177 x1 = max ((framepos_t) 0, x1 - p);
5178 x2 = max ((framepos_t) 0, x2 - p);
5179 y1 = max (0.0, y1 - y);
5180 y2 = max (0.0, y2 - y);
5182 _region_view->update_drag_selection (
5183 _editor->sample_to_pixel (x1),
5184 _editor->sample_to_pixel (x2),
5187 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5192 MidiRubberbandSelectDrag::deselect_things ()
5197 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5198 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5201 _vertical_only = true;
5205 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5207 double const y = _region_view->midi_view()->y_position ();
5209 y1 = max (0.0, y1 - y);
5210 y2 = max (0.0, y2 - y);
5212 _region_view->update_vertical_drag_selection (
5215 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5220 MidiVerticalSelectDrag::deselect_things ()
5225 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5226 : RubberbandSelectDrag (e, i)
5232 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5234 if (drag_in_progress) {
5235 /* We just want to select things at the end of the drag, not during it */
5239 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5241 _editor->begin_reversible_command (_("rubberband selection"));
5242 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5243 _editor->commit_reversible_command ();
5247 EditorRubberbandSelectDrag::deselect_things ()
5249 if (!getenv("ARDOUR_SAE")) {
5250 _editor->selection->clear_tracks();
5252 _editor->selection->clear_regions();
5253 _editor->selection->clear_points ();
5254 _editor->selection->clear_lines ();
5257 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5265 NoteCreateDrag::~NoteCreateDrag ()
5271 NoteCreateDrag::grid_frames (framepos_t t) const
5274 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
5279 return _region_view->region_beats_to_region_frames (grid_beats);
5283 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5285 Drag::start_grab (event, cursor);
5287 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5289 framepos_t pf = _drags->current_pointer_frame ();
5290 framecnt_t const g = grid_frames (pf);
5292 /* Hack so that we always snap to the note that we are over, instead of snapping
5293 to the next one if we're more than halfway through the one we're over.
5295 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5299 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5301 MidiStreamView* sv = _region_view->midi_stream_view ();
5302 double const x = _editor->sample_to_pixel (_note[0]);
5303 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5305 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5306 _drag_rect->set_outline_all ();
5307 _drag_rect->set_outline_color (0xffffff99);
5308 _drag_rect->set_fill_color (0xffffff66);
5312 NoteCreateDrag::motion (GdkEvent* event, bool)
5314 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5315 double const x = _editor->sample_to_pixel (_note[1]);
5316 if (_note[1] > _note[0]) {
5317 _drag_rect->set_x1 (x);
5319 _drag_rect->set_x0 (x);
5324 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5326 if (!had_movement) {
5330 framepos_t const start = min (_note[0], _note[1]);
5331 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5333 framecnt_t const g = grid_frames (start);
5334 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5336 if (_editor->snap_mode() == SnapNormal && length < g) {
5337 length = g - one_tick;
5340 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5342 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5346 NoteCreateDrag::y_to_region (double y) const
5349 _region_view->get_canvas_group()->canvas_to_item (x, y);
5354 NoteCreateDrag::aborted (bool)
5359 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5364 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5368 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5370 Drag::start_grab (event, cursor);
5374 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5380 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5383 distance = _drags->current_pointer_x() - grab_x();
5384 len = ar->fade_in()->back()->when;
5386 distance = grab_x() - _drags->current_pointer_x();
5387 len = ar->fade_out()->back()->when;
5390 /* how long should it be ? */
5392 new_length = len + _editor->pixel_to_sample (distance);
5394 /* now check with the region that this is legal */
5396 new_length = ar->verify_xfade_bounds (new_length, start);
5399 arv->reset_fade_in_shape_width (ar, new_length);
5401 arv->reset_fade_out_shape_width (ar, new_length);
5406 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5412 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5415 distance = _drags->current_pointer_x() - grab_x();
5416 len = ar->fade_in()->back()->when;
5418 distance = grab_x() - _drags->current_pointer_x();
5419 len = ar->fade_out()->back()->when;
5422 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5424 _editor->begin_reversible_command ("xfade trim");
5425 ar->playlist()->clear_owned_changes ();
5428 ar->set_fade_in_length (new_length);
5430 ar->set_fade_out_length (new_length);
5433 /* Adjusting the xfade may affect other regions in the playlist, so we need
5434 to get undo Commands from the whole playlist rather than just the
5438 vector<Command*> cmds;
5439 ar->playlist()->rdiff (cmds);
5440 _editor->session()->add_commands (cmds);
5441 _editor->commit_reversible_command ();
5446 CrossfadeEdgeDrag::aborted (bool)
5449 arv->redraw_start_xfade ();
5451 arv->redraw_end_xfade ();