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"
26 #include "pbd/memento_command.h"
27 #include "pbd/basename.h"
28 #include "pbd/stateful_diff_command.h"
30 #include "gtkmm2ext/utils.h"
32 #include "ardour/audioregion.h"
33 #include "ardour/dB.h"
34 #include "ardour/midi_region.h"
35 #include "ardour/operations.h"
36 #include "ardour/region_factory.h"
37 #include "ardour/session.h"
42 #include "audio_region_view.h"
43 #include "midi_region_view.h"
44 #include "ardour_ui.h"
45 #include "gui_thread.h"
46 #include "control_point.h"
48 #include "region_gain_line.h"
49 #include "editor_drag.h"
50 #include "audio_time_axis.h"
51 #include "midi_time_axis.h"
52 #include "canvas-note.h"
53 #include "selection.h"
54 #include "midi_selection.h"
55 #include "automation_time_axis.h"
57 #include "editor_cursors.h"
58 #include "mouse_cursors.h"
59 #include "verbose_cursor.h"
62 using namespace ARDOUR;
65 using namespace Gtkmm2ext;
66 using namespace Editing;
67 using namespace ArdourCanvas;
69 using Gtkmm2ext::Keyboard;
71 double ControlPointDrag::_zero_gain_fraction = -1.0;
73 DragManager::DragManager (Editor* e)
76 , _current_pointer_frame (0)
80 DragManager::~DragManager ()
85 /** Call abort for each active drag */
91 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
96 if (!_drags.empty ()) {
97 _editor->set_follow_playhead (_old_follow_playhead, false);
106 DragManager::add (Drag* d)
108 d->set_manager (this);
109 _drags.push_back (d);
113 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
115 d->set_manager (this);
116 _drags.push_back (d);
121 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
123 /* Prevent follow playhead during the drag to be nice to the user */
124 _old_follow_playhead = _editor->follow_playhead ();
125 _editor->set_follow_playhead (false);
127 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
129 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
130 (*i)->start_grab (e, c);
134 /** Call end_grab for each active drag.
135 * @return true if any drag reported movement having occurred.
138 DragManager::end_grab (GdkEvent* e)
143 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
144 bool const t = (*i)->end_grab (e);
155 _editor->set_follow_playhead (_old_follow_playhead, false);
161 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
165 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
167 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
168 bool const t = (*i)->motion_handler (e, from_autoscroll);
179 DragManager::have_item (ArdourCanvas::Item* i) const
181 list<Drag*>::const_iterator j = _drags.begin ();
182 while (j != _drags.end() && (*j)->item () != i) {
186 return j != _drags.end ();
189 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
192 , _pointer_frame_offset (0)
193 , _move_threshold_passed (false)
194 , _raw_grab_frame (0)
196 , _last_pointer_frame (0)
202 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
208 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, time);
210 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
215 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
217 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
219 if (Keyboard::is_button2_event (&event->button)) {
220 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
221 _y_constrained = true;
222 _x_constrained = false;
224 _y_constrained = false;
225 _x_constrained = true;
228 _x_constrained = false;
229 _y_constrained = false;
232 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
233 setup_pointer_frame_offset ();
234 _grab_frame = adjusted_frame (_raw_grab_frame, event);
235 _last_pointer_frame = _grab_frame;
236 _last_pointer_x = _grab_x;
237 _last_pointer_y = _grab_y;
240 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
243 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
248 if (_editor->session() && _editor->session()->transport_rolling()) {
251 _was_rolling = false;
254 switch (_editor->snap_type()) {
255 case SnapToRegionStart:
256 case SnapToRegionEnd:
257 case SnapToRegionSync:
258 case SnapToRegionBoundary:
259 _editor->build_region_boundary_cache ();
266 /** Call to end a drag `successfully'. Ungrabs item and calls
267 * subclass' finished() method.
269 * @param event GDK event, or 0.
270 * @return true if some movement occurred, otherwise false.
273 Drag::end_grab (GdkEvent* event)
275 _editor->stop_canvas_autoscroll ();
277 _item->ungrab (event ? event->button.time : 0);
279 finished (event, _move_threshold_passed);
281 _editor->verbose_cursor()->hide ();
283 return _move_threshold_passed;
287 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
291 if (f > _pointer_frame_offset) {
292 pos = f - _pointer_frame_offset;
296 _editor->snap_to_with_modifier (pos, event);
303 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
305 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
309 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
311 /* check to see if we have moved in any way that matters since the last motion event */
312 if (_move_threshold_passed &&
313 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
314 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
318 pair<framecnt_t, int> const threshold = move_threshold ();
320 bool const old_move_threshold_passed = _move_threshold_passed;
322 if (!from_autoscroll && !_move_threshold_passed) {
324 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
325 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
327 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
330 if (active (_editor->mouse_mode) && _move_threshold_passed) {
332 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
333 if (!from_autoscroll) {
334 bool const moving_left = _drags->current_pointer_x() < _last_pointer_x;
335 bool const moving_up = _drags->current_pointer_y() < _last_pointer_y;
336 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), moving_left, moving_up);
339 motion (event, _move_threshold_passed != old_move_threshold_passed);
341 _last_pointer_x = _drags->current_pointer_x ();
342 _last_pointer_y = _drags->current_pointer_y ();
343 _last_pointer_frame = adjusted_current_frame (event);
351 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
359 aborted (_move_threshold_passed);
361 _editor->stop_canvas_autoscroll ();
362 _editor->verbose_cursor()->hide ();
366 Drag::show_verbose_cursor_time (framepos_t frame)
368 _editor->verbose_cursor()->set_time (
370 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
371 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
374 _editor->verbose_cursor()->show ();
378 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
380 _editor->verbose_cursor()->show (xoffset);
382 _editor->verbose_cursor()->set_duration (
384 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
385 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
390 Drag::show_verbose_cursor_text (string const & text)
392 _editor->verbose_cursor()->show ();
394 _editor->verbose_cursor()->set (
396 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
397 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
401 boost::shared_ptr<Region>
402 Drag::add_midi_region (MidiTimeAxisView* view)
404 if (_editor->session()) {
405 const TempoMap& map (_editor->session()->tempo_map());
406 framecnt_t pos = grab_frame();
407 const Meter& m = map.meter_at (pos);
408 /* not that the frame rate used here can be affected by pull up/down which
411 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
412 return view->add_region (grab_frame(), len, true);
415 return boost::shared_ptr<Region>();
418 struct EditorOrderTimeAxisViewSorter {
419 bool operator() (TimeAxisView* a, TimeAxisView* b) {
420 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
421 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
423 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
427 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
431 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
433 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
434 as some of the regions we are dragging may be on such tracks.
437 TrackViewList track_views = _editor->track_views;
438 track_views.sort (EditorOrderTimeAxisViewSorter ());
440 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
441 _time_axis_views.push_back (*i);
443 TimeAxisView::Children children_list = (*i)->get_child_list ();
444 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
445 _time_axis_views.push_back (j->get());
449 /* the list of views can be empty at this point if this is a region list-insert drag
452 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
453 _views.push_back (DraggingView (*i, this));
456 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
460 RegionDrag::region_going_away (RegionView* v)
462 list<DraggingView>::iterator i = _views.begin ();
463 while (i != _views.end() && i->view != v) {
467 if (i != _views.end()) {
472 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
473 * or -1 if it is not found.
476 RegionDrag::find_time_axis_view (TimeAxisView* t) const
479 int const N = _time_axis_views.size ();
480 while (i < N && _time_axis_views[i] != t) {
491 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
492 : RegionDrag (e, i, p, v),
501 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
503 Drag::start_grab (event, cursor);
505 show_verbose_cursor_time (_last_frame_position);
507 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
508 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
509 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
513 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
515 /* compute the amount of pointer motion in frames, and where
516 the region would be if we moved it by that much.
518 *pending_region_position = adjusted_current_frame (event);
520 framepos_t sync_frame;
521 framecnt_t sync_offset;
524 sync_offset = _primary->region()->sync_offset (sync_dir);
526 /* we don't handle a sync point that lies before zero.
528 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
530 sync_frame = *pending_region_position + (sync_dir*sync_offset);
532 _editor->snap_to_with_modifier (sync_frame, event);
534 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
537 *pending_region_position = _last_frame_position;
540 if (*pending_region_position > max_framepos - _primary->region()->length()) {
541 *pending_region_position = _last_frame_position;
546 /* in locked edit mode, reverse the usual meaning of _x_constrained */
547 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
549 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
551 /* x movement since last time (in pixels) */
552 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
554 /* total x movement */
555 framecnt_t total_dx = *pending_region_position;
556 if (regions_came_from_canvas()) {
557 total_dx = total_dx - grab_frame ();
560 /* check that no regions have gone off the start of the session */
561 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
562 if ((i->view->region()->position() + total_dx) < 0) {
564 *pending_region_position = _last_frame_position;
569 _last_frame_position = *pending_region_position;
576 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
578 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
579 int const n = i->time_axis_view + delta_track;
580 if (n < 0 || n >= int (_time_axis_views.size())) {
581 /* off the top or bottom track */
585 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
586 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
587 /* not a track, or the wrong type */
591 double const l = i->layer + delta_layer;
593 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
594 mode to allow the user to place a region below another on layer 0.
596 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
597 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
598 If it has, the layers will be munged later anyway, so it's ok.
604 /* all regions being dragged are ok with this change */
609 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
611 assert (!_views.empty ());
613 /* Find the TimeAxisView that the pointer is now over */
614 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
616 if (first_move && tv.first->view()->layer_display() == Stacked) {
617 tv.first->view()->set_layer_display (Expanded);
620 /* Bail early if we're not over a track */
621 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
622 if (!rtv || !rtv->is_track()) {
623 _editor->verbose_cursor()->hide ();
627 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
629 /* Here's the current pointer position in terms of time axis view and layer */
630 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
631 double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
633 /* Work out the change in x */
634 framepos_t pending_region_position;
635 double const x_delta = compute_x_delta (event, &pending_region_position);
637 /* Work out the change in y */
638 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
639 double delta_layer = current_pointer_layer - _last_pointer_layer;
641 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
642 /* this y movement is not allowed, so do no y movement this time */
643 delta_time_axis_view = 0;
647 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
648 /* haven't reached next snap point, and we're not switching
649 trackviews nor layers. nothing to do.
654 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
656 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
658 RegionView* rv = i->view;
660 if (rv->region()->locked()) {
668 /* Absolutely no idea why this is necessary, but it is; without
669 it, the region view disappears after the reparent.
671 _editor->update_canvas_now ();
673 /* Reparent to a non scrolling group so that we can keep the
674 region selection above all time axis views.
675 Reparenting means that we will have to move the region view
676 later, as the two parent groups have different coordinates.
679 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
681 rv->fake_set_opaque (true);
684 /* If we have moved tracks, we'll fudge the layer delta so that the
685 region gets moved back onto layer 0 on its new track; this avoids
686 confusion when dragging regions from non-zero layers onto different
689 double this_delta_layer = delta_layer;
690 if (delta_time_axis_view != 0) {
691 this_delta_layer = - i->layer;
694 /* The TimeAxisView that this region is now on */
695 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
697 /* Ensure it is moved from stacked -> expanded if appropriate */
698 if (tv->view()->layer_display() == Stacked) {
699 tv->view()->set_layer_display (Expanded);
702 /* We're only allowed to go -ve in layer on Expanded views */
703 if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
704 this_delta_layer = - i->layer;
708 rv->set_height (tv->view()->child_height ());
710 /* Update show/hidden status as the region view may have come from a hidden track,
711 or have moved to one.
714 rv->get_canvas_group()->hide ();
716 rv->get_canvas_group()->show ();
719 /* Update the DraggingView */
720 i->time_axis_view += delta_time_axis_view;
721 i->layer += this_delta_layer;
724 _editor->mouse_brush_insert_region (rv, pending_region_position);
729 /* Get the y coordinate of the top of the track that this region is now on */
730 tv->canvas_display()->i2w (x, y);
731 y += _editor->get_trackview_group_vertical_offset();
733 /* And adjust for the layer that it should be on */
734 StreamView* cv = tv->view ();
735 switch (cv->layer_display ()) {
739 y += (cv->layers() - i->layer - 1) * cv->child_height ();
742 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
746 /* Now move the region view */
747 rv->move (x_delta, y - rv->get_canvas_group()->property_y());
750 } /* foreach region */
752 _total_x_delta += x_delta;
755 _editor->cursor_group->raise_to_top();
758 if (x_delta != 0 && !_brushing) {
759 show_verbose_cursor_time (_last_frame_position);
762 _last_pointer_time_axis_view += delta_time_axis_view;
763 _last_pointer_layer += delta_layer;
767 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
769 if (_copy && first_move) {
771 /* duplicate the regionview(s) and region(s) */
773 list<DraggingView> new_regionviews;
775 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
777 RegionView* rv = i->view;
778 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
779 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
781 const boost::shared_ptr<const Region> original = rv->region();
782 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
783 region_copy->set_position (original->position());
787 boost::shared_ptr<AudioRegion> audioregion_copy
788 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
790 nrv = new AudioRegionView (*arv, audioregion_copy);
792 boost::shared_ptr<MidiRegion> midiregion_copy
793 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
794 nrv = new MidiRegionView (*mrv, midiregion_copy);
799 nrv->get_canvas_group()->show ();
800 new_regionviews.push_back (DraggingView (nrv, this));
802 /* swap _primary to the copy */
804 if (rv == _primary) {
808 /* ..and deselect the one we copied */
810 rv->set_selected (false);
813 if (!new_regionviews.empty()) {
815 /* reflect the fact that we are dragging the copies */
817 _views = new_regionviews;
819 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
822 sync the canvas to what we think is its current state
823 without it, the canvas seems to
824 "forget" to update properly after the upcoming reparent()
825 ..only if the mouse is in rapid motion at the time of the grab.
826 something to do with regionview creation taking so long?
828 _editor->update_canvas_now();
832 RegionMotionDrag::motion (event, first_move);
836 RegionMotionDrag::finished (GdkEvent *, bool)
838 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
843 if ((*i)->view()->layer_display() == Expanded) {
844 (*i)->view()->set_layer_display (Stacked);
850 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
852 RegionMotionDrag::finished (ev, movement_occurred);
854 if (!movement_occurred) {
859 /* reverse this here so that we have the correct logic to finalize
863 if (Config->get_edit_mode() == Lock) {
864 _x_constrained = !_x_constrained;
867 assert (!_views.empty ());
869 /* We might have hidden region views so that they weren't visible during the drag
870 (when they have been reparented). Now everything can be shown again, as region
871 views are back in their track parent groups.
873 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
874 i->view->get_canvas_group()->show ();
877 bool const changed_position = (_last_frame_position != _primary->region()->position());
878 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
879 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
881 _editor->update_canvas_now ();
901 if (_editor->session() && Config->get_always_play_range()) {
902 _editor->session()->request_locate (_editor->get_selection().regions.start());
907 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
909 RegionSelection new_views;
910 PlaylistSet modified_playlists;
911 list<RegionView*> views_to_delete;
914 /* all changes were made during motion event handlers */
916 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
920 _editor->commit_reversible_command ();
924 if (_x_constrained) {
925 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
927 _editor->begin_reversible_command (Operations::region_copy);
930 /* insert the regions into their new playlists */
931 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
933 if (i->view->region()->locked()) {
939 if (changed_position && !_x_constrained) {
940 where = i->view->region()->position() - drag_delta;
942 where = i->view->region()->position();
945 RegionView* new_view = insert_region_into_playlist (
946 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
953 new_views.push_back (new_view);
955 /* we don't need the copied RegionView any more */
956 views_to_delete.push_back (i->view);
959 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
960 because when views are deleted they are automagically removed from _views, which messes
963 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
967 /* If we've created new regions either by copying or moving
968 to a new track, we want to replace the old selection with the new ones
971 if (new_views.size() > 0) {
972 _editor->selection->set (new_views);
975 /* write commands for the accumulated diffs for all our modified playlists */
976 add_stateful_diff_commands_for_playlists (modified_playlists);
978 _editor->commit_reversible_command ();
982 RegionMoveDrag::finished_no_copy (
983 bool const changed_position,
984 bool const changed_tracks,
985 framecnt_t const drag_delta
988 RegionSelection new_views;
989 PlaylistSet modified_playlists;
990 PlaylistSet frozen_playlists;
991 set<RouteTimeAxisView*> views_to_update;
994 /* all changes were made during motion event handlers */
995 _editor->commit_reversible_command ();
999 if (_x_constrained) {
1000 _editor->begin_reversible_command (_("fixed time region drag"));
1002 _editor->begin_reversible_command (Operations::region_drag);
1005 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1007 RegionView* rv = i->view;
1009 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1010 double const dest_layer = i->layer;
1012 if (rv->region()->locked()) {
1017 views_to_update.insert (dest_rtv);
1021 if (changed_position && !_x_constrained) {
1022 where = rv->region()->position() - drag_delta;
1024 where = rv->region()->position();
1027 if (changed_tracks) {
1029 /* insert into new playlist */
1031 RegionView* new_view = insert_region_into_playlist (
1032 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1035 if (new_view == 0) {
1040 new_views.push_back (new_view);
1042 /* remove from old playlist */
1044 /* the region that used to be in the old playlist is not
1045 moved to the new one - we use a copy of it. as a result,
1046 any existing editor for the region should no longer be
1049 rv->hide_region_editor();
1050 rv->fake_set_opaque (false);
1052 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1056 rv->region()->clear_changes ();
1059 motion on the same track. plonk the previously reparented region
1060 back to its original canvas group (its streamview).
1061 No need to do anything for copies as they are fake regions which will be deleted.
1064 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1065 rv->get_canvas_group()->property_y() = i->initial_y;
1068 /* just change the model */
1070 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1072 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1073 playlist->set_layer (rv->region(), dest_layer);
1076 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1078 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1081 playlist->freeze ();
1084 /* this movement may result in a crossfade being modified, so we need to get undo
1085 data from the playlist as well as the region.
1088 r = modified_playlists.insert (playlist);
1090 playlist->clear_changes ();
1093 rv->region()->set_position (where);
1095 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1098 if (changed_tracks) {
1100 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1101 was selected in all of them, then removing it from a playlist will have removed all
1102 trace of it from _views (i.e. there were N regions selected, we removed 1,
1103 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1104 corresponding regionview, and _views is now empty).
1106 This could have invalidated any and all iterators into _views.
1108 The heuristic we use here is: if the region selection is empty, break out of the loop
1109 here. if the region selection is not empty, then restart the loop because we know that
1110 we must have removed at least the region(view) we've just been working on as well as any
1111 that we processed on previous iterations.
1113 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1114 we can just iterate.
1118 if (_views.empty()) {
1129 /* If we've created new regions either by copying or moving
1130 to a new track, we want to replace the old selection with the new ones
1133 if (new_views.size() > 0) {
1134 _editor->selection->set (new_views);
1137 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1141 /* write commands for the accumulated diffs for all our modified playlists */
1142 add_stateful_diff_commands_for_playlists (modified_playlists);
1144 _editor->commit_reversible_command ();
1146 /* We have futzed with the layering of canvas items on our streamviews.
1147 If any region changed layer, this will have resulted in the stream
1148 views being asked to set up their region views, and all will be well.
1149 If not, we might now have badly-ordered region views. Ask the StreamViews
1150 involved to sort themselves out, just in case.
1153 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1154 (*i)->view()->playlist_layered ((*i)->track ());
1158 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1159 * @param region Region to remove.
1160 * @param playlist playlist To remove from.
1161 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1162 * that clear_changes () is only called once per playlist.
1165 RegionMoveDrag::remove_region_from_playlist (
1166 boost::shared_ptr<Region> region,
1167 boost::shared_ptr<Playlist> playlist,
1168 PlaylistSet& modified_playlists
1171 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1174 playlist->clear_changes ();
1177 playlist->remove_region (region);
1181 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1182 * clearing the playlist's diff history first if necessary.
1183 * @param region Region to insert.
1184 * @param dest_rtv Destination RouteTimeAxisView.
1185 * @param dest_layer Destination layer.
1186 * @param where Destination position.
1187 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1188 * that clear_changes () is only called once per playlist.
1189 * @return New RegionView, or 0 if no insert was performed.
1192 RegionMoveDrag::insert_region_into_playlist (
1193 boost::shared_ptr<Region> region,
1194 RouteTimeAxisView* dest_rtv,
1197 PlaylistSet& modified_playlists
1200 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1201 if (!dest_playlist) {
1205 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1206 _new_region_view = 0;
1207 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1209 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1210 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1212 dest_playlist->clear_changes ();
1215 dest_playlist->add_region (region, where);
1217 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1218 dest_playlist->set_layer (region, dest_layer);
1223 assert (_new_region_view);
1225 return _new_region_view;
1229 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1231 _new_region_view = rv;
1235 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1237 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1238 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1240 _editor->session()->add_command (c);
1249 RegionMoveDrag::aborted (bool movement_occurred)
1253 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1260 RegionMotionDrag::aborted (movement_occurred);
1265 RegionMotionDrag::aborted (bool)
1267 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1268 if ((*i)->view()->layer_display() == Expanded) {
1269 (*i)->view()->set_layer_display (Stacked);
1273 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1274 RegionView* rv = i->view;
1275 TimeAxisView* tv = &(rv->get_time_axis_view ());
1276 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1278 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1279 rv->get_canvas_group()->property_y() = 0;
1281 rv->fake_set_opaque (false);
1282 rv->move (-_total_x_delta, 0);
1283 rv->set_height (rtv->view()->child_height ());
1286 _editor->update_canvas_now ();
1289 /** @param b true to brush, otherwise false.
1290 * @param c true to make copies of the regions being moved, otherwise false.
1292 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1293 : RegionMotionDrag (e, i, p, v, b),
1296 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1299 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1300 if (rtv && rtv->is_track()) {
1301 speed = rtv->track()->speed ();
1304 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1308 RegionMoveDrag::setup_pointer_frame_offset ()
1310 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1313 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1314 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1316 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1318 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1319 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1321 _primary = v->view()->create_region_view (r, false, false);
1323 _primary->get_canvas_group()->show ();
1324 _primary->set_position (pos, 0);
1325 _views.push_back (DraggingView (_primary, this));
1327 _last_frame_position = pos;
1329 _item = _primary->get_canvas_group ();
1333 RegionInsertDrag::finished (GdkEvent *, bool)
1335 _editor->update_canvas_now ();
1337 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1339 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1340 _primary->get_canvas_group()->property_y() = 0;
1342 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1344 _editor->begin_reversible_command (Operations::insert_region);
1345 playlist->clear_changes ();
1346 playlist->add_region (_primary->region (), _last_frame_position);
1347 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1348 _editor->commit_reversible_command ();
1356 RegionInsertDrag::aborted (bool)
1363 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1364 : RegionMoveDrag (e, i, p, v, false, false)
1366 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1369 struct RegionSelectionByPosition {
1370 bool operator() (RegionView*a, RegionView* b) {
1371 return a->region()->position () < b->region()->position();
1376 RegionSpliceDrag::motion (GdkEvent* event, bool)
1378 /* Which trackview is this ? */
1380 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1381 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1383 /* The region motion is only processed if the pointer is over
1387 if (!tv || !tv->is_track()) {
1388 /* To make sure we hide the verbose canvas cursor when the mouse is
1389 not held over and audiotrack.
1391 _editor->verbose_cursor()->hide ();
1397 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1403 RegionSelection copy (_editor->selection->regions);
1405 RegionSelectionByPosition cmp;
1408 framepos_t const pf = adjusted_current_frame (event);
1410 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1412 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1418 boost::shared_ptr<Playlist> playlist;
1420 if ((playlist = atv->playlist()) == 0) {
1424 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1429 if (pf < (*i)->region()->last_frame() + 1) {
1433 if (pf > (*i)->region()->first_frame()) {
1439 playlist->shuffle ((*i)->region(), dir);
1444 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1446 RegionMoveDrag::finished (event, movement_occurred);
1450 RegionSpliceDrag::aborted (bool)
1455 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1457 _view (dynamic_cast<MidiTimeAxisView*> (v))
1459 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1465 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1468 _region = add_midi_region (_view);
1469 _view->playlist()->freeze ();
1472 framepos_t const f = adjusted_current_frame (event);
1473 if (f < grab_frame()) {
1474 _region->set_position (f);
1477 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1478 so that if this region is duplicated, its duplicate starts on
1479 a snap point rather than 1 frame after a snap point. Otherwise things get
1480 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1481 place snapped notes at the start of the region.
1484 framecnt_t const len = abs (f - grab_frame () - 1);
1485 _region->set_length (len < 1 ? 1 : len);
1491 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1493 if (!movement_occurred) {
1494 add_midi_region (_view);
1496 _view->playlist()->thaw ();
1501 RegionCreateDrag::aborted (bool)
1504 _view->playlist()->thaw ();
1510 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1514 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1518 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1520 Gdk::Cursor* cursor;
1521 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1522 float x_fraction = cnote->mouse_x_fraction ();
1524 if (x_fraction > 0.0 && x_fraction < 0.25) {
1525 cursor = _editor->cursors()->left_side_trim;
1527 cursor = _editor->cursors()->right_side_trim;
1530 Drag::start_grab (event, cursor);
1532 region = &cnote->region_view();
1534 double const region_start = region->get_position_pixels();
1535 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1537 if (grab_x() <= middle_point) {
1538 cursor = _editor->cursors()->left_side_trim;
1541 cursor = _editor->cursors()->right_side_trim;
1545 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1547 if (event->motion.state & Keyboard::PrimaryModifier) {
1553 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1555 if (ms.size() > 1) {
1556 /* has to be relative, may make no sense otherwise */
1560 /* select this note; if it is already selected, preserve the existing selection,
1561 otherwise make this note the only one selected.
1563 region->note_selected (cnote, cnote->selected ());
1565 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1566 MidiRegionSelection::iterator next;
1569 (*r)->begin_resizing (at_front);
1575 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1577 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1578 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1579 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1584 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1586 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1587 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1588 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1593 NoteResizeDrag::aborted (bool)
1595 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1596 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1597 (*r)->abort_resizing ();
1601 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1602 : RegionDrag (e, i, p, v)
1604 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1608 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1611 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1612 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1614 if (tv && tv->is_track()) {
1615 speed = tv->track()->speed();
1618 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1619 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1620 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1622 framepos_t const pf = adjusted_current_frame (event);
1624 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1625 /* Move the contents of the region around without changing the region bounds */
1626 _operation = ContentsTrim;
1627 Drag::start_grab (event, _editor->cursors()->trimmer);
1629 /* These will get overridden for a point trim.*/
1630 if (pf < (region_start + region_length/2)) {
1631 /* closer to front */
1632 _operation = StartTrim;
1633 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1636 _operation = EndTrim;
1637 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1641 switch (_operation) {
1643 show_verbose_cursor_time (region_start);
1644 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1645 i->view->trim_front_starting ();
1649 show_verbose_cursor_time (region_end);
1652 show_verbose_cursor_time (pf);
1656 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1657 i->view->region()->suspend_property_changes ();
1662 TrimDrag::motion (GdkEvent* event, bool first_move)
1664 RegionView* rv = _primary;
1667 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1668 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1669 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1671 if (tv && tv->is_track()) {
1672 speed = tv->track()->speed();
1675 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1681 switch (_operation) {
1683 trim_type = "Region start trim";
1686 trim_type = "Region end trim";
1689 trim_type = "Region content trim";
1693 _editor->begin_reversible_command (trim_type);
1695 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1696 RegionView* rv = i->view;
1697 rv->fake_set_opaque (false);
1698 rv->enable_display (false);
1699 rv->region()->playlist()->clear_owned_changes ();
1701 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1704 arv->temporarily_hide_envelope ();
1708 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1709 insert_result = _editor->motion_frozen_playlists.insert (pl);
1711 if (insert_result.second) {
1717 bool non_overlap_trim = false;
1719 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1720 non_overlap_trim = true;
1723 switch (_operation) {
1725 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1726 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1731 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1732 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1738 bool swap_direction = false;
1740 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1741 swap_direction = true;
1744 framecnt_t frame_delta = 0;
1746 bool left_direction = false;
1747 if (last_pointer_frame() > adjusted_current_frame(event)) {
1748 left_direction = true;
1751 if (left_direction) {
1752 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1754 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1757 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1758 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1764 switch (_operation) {
1766 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1769 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1772 show_verbose_cursor_time (adjusted_current_frame (event));
1779 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1781 if (movement_occurred) {
1782 motion (event, false);
1784 /* This must happen before the region's StatefulDiffCommand is created, as it may
1785 `correct' (ahem) the region's _start from being negative to being zero. It
1786 needs to be zero in the undo record.
1788 if (_operation == StartTrim) {
1789 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1790 i->view->trim_front_ending ();
1794 if (!_editor->selection->selected (_primary)) {
1795 _primary->thaw_after_trim ();
1798 set<boost::shared_ptr<Playlist> > diffed_playlists;
1800 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1801 i->view->thaw_after_trim ();
1802 i->view->enable_display (true);
1803 i->view->fake_set_opaque (true);
1805 /* Trimming one region may affect others on the playlist, so we need
1806 to get undo Commands from the whole playlist rather than just the
1807 region. Use diffed_playlists to make sure we don't diff a given
1808 playlist more than once.
1810 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1811 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1812 vector<Command*> cmds;
1814 _editor->session()->add_commands (cmds);
1815 diffed_playlists.insert (p);
1819 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1823 _editor->motion_frozen_playlists.clear ();
1824 _editor->commit_reversible_command();
1827 /* no mouse movement */
1828 _editor->point_trim (event, adjusted_current_frame (event));
1831 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1832 if (_operation == StartTrim) {
1833 i->view->trim_front_ending ();
1836 i->view->region()->resume_property_changes ();
1841 TrimDrag::aborted (bool movement_occurred)
1843 /* Our motion method is changing model state, so use the Undo system
1844 to cancel. Perhaps not ideal, as this will leave an Undo point
1845 behind which may be slightly odd from the user's point of view.
1850 if (movement_occurred) {
1854 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1855 i->view->region()->resume_property_changes ();
1860 TrimDrag::setup_pointer_frame_offset ()
1862 list<DraggingView>::iterator i = _views.begin ();
1863 while (i != _views.end() && i->view != _primary) {
1867 if (i == _views.end()) {
1871 switch (_operation) {
1873 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1876 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1883 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1887 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1888 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1893 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1895 Drag::start_grab (event, cursor);
1896 show_verbose_cursor_time (adjusted_current_frame(event));
1900 MeterMarkerDrag::setup_pointer_frame_offset ()
1902 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1906 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
1910 // create a dummy marker for visual representation of moving the
1911 // section, because whether its a copy or not, we're going to
1912 // leave or lose the original marker (leave if its a copy; lose if its
1913 // not, because we'll remove it from the map).
1915 MeterSection section (_marker->meter());
1917 if (!section.movable()) {
1922 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
1924 _marker = new MeterMarker (
1926 *_editor->meter_group,
1927 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1929 *new MeterSection (_marker->meter())
1932 /* use the new marker for the grab */
1933 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
1936 TempoMap& map (_editor->session()->tempo_map());
1937 /* get current state */
1938 before_state = &map.get_state();
1939 /* remove the section while we drag it */
1940 map.remove_meter (section, true);
1944 framepos_t const pf = adjusted_current_frame (event);
1945 _marker->set_position (pf);
1946 show_verbose_cursor_time (pf);
1950 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1952 if (!movement_occurred) {
1956 motion (event, false);
1958 Timecode::BBT_Time when;
1960 TempoMap& map (_editor->session()->tempo_map());
1961 map.bbt_time (last_pointer_frame(), when);
1963 if (_copy == true) {
1964 _editor->begin_reversible_command (_("copy meter mark"));
1965 XMLNode &before = map.get_state();
1966 map.add_meter (_marker->meter(), when);
1967 XMLNode &after = map.get_state();
1968 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1969 _editor->commit_reversible_command ();
1972 _editor->begin_reversible_command (_("move meter mark"));
1974 /* we removed it before, so add it back now */
1976 map.add_meter (_marker->meter(), when);
1977 XMLNode &after = map.get_state();
1978 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
1979 _editor->commit_reversible_command ();
1982 // delete the dummy marker we used for visual representation while moving.
1983 // a new visual marker will show up automatically.
1988 MeterMarkerDrag::aborted (bool moved)
1990 _marker->set_position (_marker->meter().frame ());
1993 TempoMap& map (_editor->session()->tempo_map());
1994 /* we removed it before, so add it back now */
1995 map.add_meter (_marker->meter(), _marker->meter().frame());
1996 // delete the dummy marker we used for visual representation while moving.
1997 // a new visual marker will show up automatically.
2002 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2006 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2008 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2013 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2015 Drag::start_grab (event, cursor);
2016 show_verbose_cursor_time (adjusted_current_frame (event));
2020 TempoMarkerDrag::setup_pointer_frame_offset ()
2022 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2026 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2030 // create a dummy marker for visual representation of moving the
2031 // section, because whether its a copy or not, we're going to
2032 // leave or lose the original marker (leave if its a copy; lose if its
2033 // not, because we'll remove it from the map).
2035 // create a dummy marker for visual representation of moving the copy.
2036 // The actual copying is not done before we reach the finish callback.
2039 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2041 TempoSection section (_marker->tempo());
2043 _marker = new TempoMarker (
2045 *_editor->tempo_group,
2046 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
2048 *new TempoSection (_marker->tempo())
2051 /* use the new marker for the grab */
2052 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2055 TempoMap& map (_editor->session()->tempo_map());
2056 /* get current state */
2057 before_state = &map.get_state();
2058 /* remove the section while we drag it */
2059 map.remove_tempo (section, true);
2063 framepos_t const pf = adjusted_current_frame (event);
2064 _marker->set_position (pf);
2065 show_verbose_cursor_time (pf);
2069 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2071 if (!movement_occurred) {
2075 motion (event, false);
2077 TempoMap& map (_editor->session()->tempo_map());
2078 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2079 Timecode::BBT_Time when;
2081 map.bbt_time (beat_time, when);
2083 if (_copy == true) {
2084 _editor->begin_reversible_command (_("copy tempo mark"));
2085 XMLNode &before = map.get_state();
2086 map.add_tempo (_marker->tempo(), when);
2087 XMLNode &after = map.get_state();
2088 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2089 _editor->commit_reversible_command ();
2092 _editor->begin_reversible_command (_("move tempo mark"));
2093 /* we removed it before, so add it back now */
2094 map.add_tempo (_marker->tempo(), when);
2095 XMLNode &after = map.get_state();
2096 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2097 _editor->commit_reversible_command ();
2100 // delete the dummy marker we used for visual representation while moving.
2101 // a new visual marker will show up automatically.
2106 TempoMarkerDrag::aborted (bool moved)
2108 _marker->set_position (_marker->tempo().frame());
2110 TempoMap& map (_editor->session()->tempo_map());
2111 /* we removed it before, so add it back now */
2112 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2113 // delete the dummy marker we used for visual representation while moving.
2114 // a new visual marker will show up automatically.
2119 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2123 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2126 /** Do all the things we do when dragging the playhead to make it look as though
2127 * we have located, without actually doing the locate (because that would cause
2128 * the diskstream buffers to be refilled, which is too slow).
2131 CursorDrag::fake_locate (framepos_t t)
2133 _editor->playhead_cursor->set_position (t);
2135 Session* s = _editor->session ();
2136 if (s->timecode_transmission_suspended ()) {
2137 framepos_t const f = _editor->playhead_cursor->current_frame;
2138 s->send_mmc_locate (f);
2139 s->send_full_time_code (f);
2142 show_verbose_cursor_time (t);
2143 _editor->UpdateAllTransportClocks (t);
2147 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2149 Drag::start_grab (event, c);
2151 _grab_zoom = _editor->frames_per_unit;
2153 framepos_t where = _editor->event_frame (event, 0, 0);
2154 _editor->snap_to_with_modifier (where, event);
2156 _editor->_dragging_playhead = true;
2158 Session* s = _editor->session ();
2161 if (_was_rolling && _stop) {
2165 if (s->is_auditioning()) {
2166 s->cancel_audition ();
2169 s->request_suspend_timecode_transmission ();
2170 while (!s->timecode_transmission_suspended ()) {
2171 /* twiddle our thumbs */
2175 fake_locate (where);
2179 CursorDrag::motion (GdkEvent* event, bool)
2181 framepos_t const adjusted_frame = adjusted_current_frame (event);
2182 if (adjusted_frame != last_pointer_frame()) {
2183 fake_locate (adjusted_frame);
2185 _editor->update_canvas_now ();
2191 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2193 _editor->_dragging_playhead = false;
2195 if (!movement_occurred && _stop) {
2199 motion (event, false);
2201 Session* s = _editor->session ();
2203 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2204 _editor->_pending_locate_request = true;
2205 s->request_resume_timecode_transmission ();
2210 CursorDrag::aborted (bool)
2212 if (_editor->_dragging_playhead) {
2213 _editor->session()->request_resume_timecode_transmission ();
2214 _editor->_dragging_playhead = false;
2217 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2220 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2221 : RegionDrag (e, i, p, v)
2223 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2227 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2229 Drag::start_grab (event, cursor);
2231 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2232 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2234 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2236 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2240 FadeInDrag::setup_pointer_frame_offset ()
2242 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2243 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2244 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2248 FadeInDrag::motion (GdkEvent* event, bool)
2250 framecnt_t fade_length;
2252 framepos_t const pos = adjusted_current_frame (event);
2254 boost::shared_ptr<Region> region = _primary->region ();
2256 if (pos < (region->position() + 64)) {
2257 fade_length = 64; // this should be a minimum defined somewhere
2258 } else if (pos > region->last_frame()) {
2259 fade_length = region->length();
2261 fade_length = pos - region->position();
2264 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2266 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2272 tmp->reset_fade_in_shape_width (fade_length);
2273 tmp->show_fade_line((framecnt_t) fade_length);
2276 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2280 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2282 if (!movement_occurred) {
2286 framecnt_t fade_length;
2288 framepos_t const pos = adjusted_current_frame (event);
2290 boost::shared_ptr<Region> region = _primary->region ();
2292 if (pos < (region->position() + 64)) {
2293 fade_length = 64; // this should be a minimum defined somewhere
2294 } else if (pos > region->last_frame()) {
2295 fade_length = region->length();
2297 fade_length = pos - region->position();
2300 _editor->begin_reversible_command (_("change fade in length"));
2302 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2304 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2310 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2311 XMLNode &before = alist->get_state();
2313 tmp->audio_region()->set_fade_in_length (fade_length);
2314 tmp->audio_region()->set_fade_in_active (true);
2315 tmp->hide_fade_line();
2317 XMLNode &after = alist->get_state();
2318 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2321 _editor->commit_reversible_command ();
2325 FadeInDrag::aborted (bool)
2327 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2328 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2334 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2335 tmp->hide_fade_line();
2339 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2340 : RegionDrag (e, i, p, v)
2342 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2346 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2348 Drag::start_grab (event, cursor);
2350 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2351 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2353 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2355 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2359 FadeOutDrag::setup_pointer_frame_offset ()
2361 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2362 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2363 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2367 FadeOutDrag::motion (GdkEvent* event, bool)
2369 framecnt_t fade_length;
2371 framepos_t const pos = adjusted_current_frame (event);
2373 boost::shared_ptr<Region> region = _primary->region ();
2375 if (pos > (region->last_frame() - 64)) {
2376 fade_length = 64; // this should really be a minimum fade defined somewhere
2378 else if (pos < region->position()) {
2379 fade_length = region->length();
2382 fade_length = region->last_frame() - pos;
2385 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2387 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2393 tmp->reset_fade_out_shape_width (fade_length);
2394 tmp->show_fade_line(region->length() - fade_length);
2397 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2401 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2403 if (!movement_occurred) {
2407 framecnt_t fade_length;
2409 framepos_t const pos = adjusted_current_frame (event);
2411 boost::shared_ptr<Region> region = _primary->region ();
2413 if (pos > (region->last_frame() - 64)) {
2414 fade_length = 64; // this should really be a minimum fade defined somewhere
2416 else if (pos < region->position()) {
2417 fade_length = region->length();
2420 fade_length = region->last_frame() - pos;
2423 _editor->begin_reversible_command (_("change fade out length"));
2425 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2427 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2433 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2434 XMLNode &before = alist->get_state();
2436 tmp->audio_region()->set_fade_out_length (fade_length);
2437 tmp->audio_region()->set_fade_out_active (true);
2438 tmp->hide_fade_line();
2440 XMLNode &after = alist->get_state();
2441 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2444 _editor->commit_reversible_command ();
2448 FadeOutDrag::aborted (bool)
2450 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2451 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2457 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2458 tmp->hide_fade_line();
2462 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2465 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2467 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2470 _points.push_back (Gnome::Art::Point (0, 0));
2471 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2474 MarkerDrag::~MarkerDrag ()
2476 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2482 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2484 Drag::start_grab (event, cursor);
2488 Location *location = _editor->find_location_from_marker (_marker, is_start);
2489 _editor->_dragging_edit_point = true;
2491 update_item (location);
2493 // _drag_line->show();
2494 // _line->raise_to_top();
2497 show_verbose_cursor_time (location->start());
2499 show_verbose_cursor_time (location->end());
2502 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2505 case Selection::Toggle:
2506 _editor->selection->toggle (_marker);
2508 case Selection::Set:
2509 if (!_editor->selection->selected (_marker)) {
2510 _editor->selection->set (_marker);
2513 case Selection::Extend:
2515 Locations::LocationList ll;
2516 list<Marker*> to_add;
2518 _editor->selection->markers.range (s, e);
2519 s = min (_marker->position(), s);
2520 e = max (_marker->position(), e);
2523 if (e < max_framepos) {
2526 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2527 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2528 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2531 to_add.push_back (lm->start);
2534 to_add.push_back (lm->end);
2538 if (!to_add.empty()) {
2539 _editor->selection->add (to_add);
2543 case Selection::Add:
2544 _editor->selection->add (_marker);
2548 /* Set up copies for us to manipulate during the drag */
2550 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2551 Location* l = _editor->find_location_from_marker (*i, is_start);
2552 _copied_locations.push_back (new Location (*l));
2557 MarkerDrag::setup_pointer_frame_offset ()
2560 Location *location = _editor->find_location_from_marker (_marker, is_start);
2561 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2565 MarkerDrag::motion (GdkEvent* event, bool)
2567 framecnt_t f_delta = 0;
2569 bool move_both = false;
2571 Location *real_location;
2572 Location *copy_location = 0;
2574 framepos_t const newframe = adjusted_current_frame (event);
2576 framepos_t next = newframe;
2578 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2582 MarkerSelection::iterator i;
2583 list<Location*>::iterator x;
2585 /* find the marker we're dragging, and compute the delta */
2587 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2588 x != _copied_locations.end() && i != _editor->selection->markers.end();
2594 if (marker == _marker) {
2596 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2601 if (real_location->is_mark()) {
2602 f_delta = newframe - copy_location->start();
2606 switch (marker->type()) {
2607 case Marker::SessionStart:
2608 case Marker::RangeStart:
2609 case Marker::LoopStart:
2610 case Marker::PunchIn:
2611 f_delta = newframe - copy_location->start();
2614 case Marker::SessionEnd:
2615 case Marker::RangeEnd:
2616 case Marker::LoopEnd:
2617 case Marker::PunchOut:
2618 f_delta = newframe - copy_location->end();
2621 /* what kind of marker is this ? */
2629 if (i == _editor->selection->markers.end()) {
2630 /* hmm, impossible - we didn't find the dragged marker */
2634 /* now move them all */
2636 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2637 x != _copied_locations.end() && i != _editor->selection->markers.end();
2643 /* call this to find out if its the start or end */
2645 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2649 if (real_location->locked()) {
2653 if (copy_location->is_mark()) {
2657 copy_location->set_start (copy_location->start() + f_delta);
2661 framepos_t new_start = copy_location->start() + f_delta;
2662 framepos_t new_end = copy_location->end() + f_delta;
2664 if (is_start) { // start-of-range marker
2667 copy_location->set_start (new_start);
2668 copy_location->set_end (new_end);
2669 } else if (new_start < copy_location->end()) {
2670 copy_location->set_start (new_start);
2671 } else if (newframe > 0) {
2672 _editor->snap_to (next, 1, true);
2673 copy_location->set_end (next);
2674 copy_location->set_start (newframe);
2677 } else { // end marker
2680 copy_location->set_end (new_end);
2681 copy_location->set_start (new_start);
2682 } else if (new_end > copy_location->start()) {
2683 copy_location->set_end (new_end);
2684 } else if (newframe > 0) {
2685 _editor->snap_to (next, -1, true);
2686 copy_location->set_start (next);
2687 copy_location->set_end (newframe);
2692 update_item (copy_location);
2694 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2697 lm->set_position (copy_location->start(), copy_location->end());
2701 assert (!_copied_locations.empty());
2703 show_verbose_cursor_time (newframe);
2706 _editor->update_canvas_now ();
2711 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2713 if (!movement_occurred) {
2715 /* just a click, do nothing but finish
2716 off the selection process
2719 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2722 case Selection::Set:
2723 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2724 _editor->selection->set (_marker);
2728 case Selection::Toggle:
2729 case Selection::Extend:
2730 case Selection::Add:
2737 _editor->_dragging_edit_point = false;
2739 _editor->begin_reversible_command ( _("move marker") );
2740 XMLNode &before = _editor->session()->locations()->get_state();
2742 MarkerSelection::iterator i;
2743 list<Location*>::iterator x;
2746 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2747 x != _copied_locations.end() && i != _editor->selection->markers.end();
2750 Location * location = _editor->find_location_from_marker (*i, is_start);
2754 if (location->locked()) {
2758 if (location->is_mark()) {
2759 location->set_start ((*x)->start());
2761 location->set ((*x)->start(), (*x)->end());
2766 XMLNode &after = _editor->session()->locations()->get_state();
2767 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2768 _editor->commit_reversible_command ();
2772 MarkerDrag::aborted (bool)
2778 MarkerDrag::update_item (Location*)
2783 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2785 _cumulative_x_drag (0),
2786 _cumulative_y_drag (0)
2788 if (_zero_gain_fraction < 0.0) {
2789 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2792 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2794 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2800 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2802 Drag::start_grab (event, _editor->cursors()->fader);
2804 // start the grab at the center of the control point so
2805 // the point doesn't 'jump' to the mouse after the first drag
2806 _fixed_grab_x = _point->get_x();
2807 _fixed_grab_y = _point->get_y();
2809 float const fraction = 1 - (_point->get_y() / _point->line().height());
2811 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2813 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2814 event->button.x + 10, event->button.y + 10);
2816 _editor->verbose_cursor()->show ();
2818 if (!_point->can_slide ()) {
2819 _x_constrained = true;
2824 ControlPointDrag::motion (GdkEvent* event, bool)
2826 double dx = _drags->current_pointer_x() - last_pointer_x();
2827 double dy = _drags->current_pointer_y() - last_pointer_y();
2829 if (event->button.state & Keyboard::SecondaryModifier) {
2834 /* coordinate in pixels relative to the start of the region (for region-based automation)
2835 or track (for track-based automation) */
2836 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2837 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2839 // calculate zero crossing point. back off by .01 to stay on the
2840 // positive side of zero
2841 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2843 // make sure we hit zero when passing through
2844 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2848 if (_x_constrained) {
2851 if (_y_constrained) {
2855 _cumulative_x_drag = cx - _fixed_grab_x;
2856 _cumulative_y_drag = cy - _fixed_grab_y;
2860 cy = min ((double) _point->line().height(), cy);
2862 framepos_t cx_frames = _editor->unit_to_frame (cx);
2864 if (!_x_constrained) {
2865 _editor->snap_to_with_modifier (cx_frames, event);
2868 cx_frames = min (cx_frames, _point->line().maximum_time());
2870 float const fraction = 1.0 - (cy / _point->line().height());
2872 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2874 _point->line().drag_motion (_editor->frame_to_unit_unrounded (cx_frames), fraction, false, push);
2876 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2880 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2882 if (!movement_occurred) {
2886 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2887 _editor->reset_point_selection ();
2891 motion (event, false);
2894 _point->line().end_drag ();
2895 _editor->session()->commit_reversible_command ();
2899 ControlPointDrag::aborted (bool)
2901 _point->line().reset ();
2905 ControlPointDrag::active (Editing::MouseMode m)
2907 if (m == Editing::MouseGain) {
2908 /* always active in mouse gain */
2912 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2913 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2916 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2919 _cumulative_y_drag (0)
2921 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2925 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2927 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2930 _item = &_line->grab_item ();
2932 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2933 origin, and ditto for y.
2936 double cx = event->button.x;
2937 double cy = event->button.y;
2939 _line->parent_group().w2i (cx, cy);
2941 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2946 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2947 /* no adjacent points */
2951 Drag::start_grab (event, _editor->cursors()->fader);
2953 /* store grab start in parent frame */
2958 double fraction = 1.0 - (cy / _line->height());
2960 _line->start_drag_line (before, after, fraction);
2962 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2963 event->button.x + 10, event->button.y + 10);
2965 _editor->verbose_cursor()->show ();
2969 LineDrag::motion (GdkEvent* event, bool)
2971 double dy = _drags->current_pointer_y() - last_pointer_y();
2973 if (event->button.state & Keyboard::SecondaryModifier) {
2977 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2979 _cumulative_y_drag = cy - _fixed_grab_y;
2982 cy = min ((double) _line->height(), cy);
2984 double const fraction = 1.0 - (cy / _line->height());
2985 bool const push = !Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2987 /* we are ignoring x position for this drag, so we can just pass in anything */
2988 _line->drag_motion (0, fraction, true, push);
2990 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
2994 LineDrag::finished (GdkEvent* event, bool)
2996 motion (event, false);
2998 _editor->session()->commit_reversible_command ();
3002 LineDrag::aborted (bool)
3007 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3010 _cumulative_x_drag (0)
3012 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3016 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3018 Drag::start_grab (event);
3020 _line = reinterpret_cast<Line*> (_item);
3023 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3025 double cx = event->button.x;
3026 double cy = event->button.y;
3028 _item->property_parent().get_value()->w2i(cx, cy);
3030 /* store grab start in parent frame */
3031 _region_view_grab_x = cx;
3033 _before = *(float*) _item->get_data ("position");
3035 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3037 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3041 FeatureLineDrag::motion (GdkEvent*, bool)
3043 double dx = _drags->current_pointer_x() - last_pointer_x();
3045 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3047 _cumulative_x_drag += dx;
3049 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3058 ArdourCanvas::Points points;
3060 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3062 _line->get_bounds(x1, y2, x2, y2);
3064 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3065 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3067 _line->property_points() = points;
3069 float *pos = new float;
3072 _line->set_data ("position", pos);
3078 FeatureLineDrag::finished (GdkEvent*, bool)
3080 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3081 _arv->update_transient(_before, _before);
3085 FeatureLineDrag::aborted (bool)
3090 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3092 , _vertical_only (false)
3094 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3098 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3100 Drag::start_grab (event);
3101 show_verbose_cursor_time (adjusted_current_frame (event));
3105 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3112 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3114 framepos_t grab = grab_frame ();
3115 if (Config->get_rubberbanding_snaps_to_grid ()) {
3116 _editor->snap_to_with_modifier (grab, event);
3119 /* base start and end on initial click position */
3129 if (_drags->current_pointer_y() < grab_y()) {
3130 y1 = _drags->current_pointer_y();
3133 y2 = _drags->current_pointer_y();
3138 if (start != end || y1 != y2) {
3140 double x1 = _editor->frame_to_pixel (start);
3141 double x2 = _editor->frame_to_pixel (end);
3143 _editor->rubberband_rect->property_x1() = x1;
3144 if (_vertical_only) {
3145 /* fixed 10 pixel width */
3146 _editor->rubberband_rect->property_x2() = x1 + 10;
3148 _editor->rubberband_rect->property_x2() = x2;
3151 _editor->rubberband_rect->property_y1() = y1;
3152 _editor->rubberband_rect->property_y2() = y2;
3154 _editor->rubberband_rect->show();
3155 _editor->rubberband_rect->raise_to_top();
3157 show_verbose_cursor_time (pf);
3159 do_select_things (event, true);
3164 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3169 if (grab_frame() < last_pointer_frame()) {
3171 x2 = last_pointer_frame ();
3174 x1 = last_pointer_frame ();
3180 if (_drags->current_pointer_y() < grab_y()) {
3181 y1 = _drags->current_pointer_y();
3184 y2 = _drags->current_pointer_y();
3188 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3192 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3194 if (movement_occurred) {
3196 motion (event, false);
3197 do_select_things (event, false);
3203 bool do_deselect = true;
3204 MidiTimeAxisView* mtv;
3206 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3208 if (_editor->selection->empty()) {
3209 /* nothing selected */
3210 add_midi_region (mtv);
3211 do_deselect = false;
3221 _editor->rubberband_rect->hide();
3225 RubberbandSelectDrag::aborted (bool)
3227 _editor->rubberband_rect->hide ();
3230 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3231 : RegionDrag (e, i, p, v)
3233 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3237 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3239 Drag::start_grab (event, cursor);
3241 show_verbose_cursor_time (adjusted_current_frame (event));
3245 TimeFXDrag::motion (GdkEvent* event, bool)
3247 RegionView* rv = _primary;
3248 StreamView* cv = rv->get_time_axis_view().view ();
3250 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3251 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3252 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3254 framepos_t const pf = adjusted_current_frame (event);
3256 if (pf > rv->region()->position()) {
3257 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3260 show_verbose_cursor_time (pf);
3264 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3266 _primary->get_time_axis_view().hide_timestretch ();
3268 if (!movement_occurred) {
3272 if (last_pointer_frame() < _primary->region()->position()) {
3273 /* backwards drag of the left edge - not usable */
3277 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3279 float percentage = (double) newlen / (double) _primary->region()->length();
3281 #ifndef USE_RUBBERBAND
3282 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3283 if (_primary->region()->data_type() == DataType::AUDIO) {
3284 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3288 if (!_editor->get_selection().regions.empty()) {
3289 /* primary will already be included in the selection, and edit
3290 group shared editing will propagate selection across
3291 equivalent regions, so just use the current region
3295 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3296 error << _("An error occurred while executing time stretch operation") << endmsg;
3302 TimeFXDrag::aborted (bool)
3304 _primary->get_time_axis_view().hide_timestretch ();
3307 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3310 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3314 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3316 Drag::start_grab (event);
3320 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3322 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3326 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3328 if (movement_occurred && _editor->session()) {
3329 /* make sure we stop */
3330 _editor->session()->request_transport_speed (0.0);
3335 ScrubDrag::aborted (bool)
3340 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3344 , _original_pointer_time_axis (-1)
3345 , _last_pointer_time_axis (-1)
3346 , _time_selection_at_start (!_editor->get_selection().time.empty())
3348 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3352 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3354 if (_editor->session() == 0) {
3358 Gdk::Cursor* cursor = 0;
3360 switch (_operation) {
3361 case CreateSelection:
3362 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3367 cursor = _editor->cursors()->selector;
3368 Drag::start_grab (event, cursor);
3371 case SelectionStartTrim:
3372 if (_editor->clicked_axisview) {
3373 _editor->clicked_axisview->order_selection_trims (_item, true);
3375 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3378 case SelectionEndTrim:
3379 if (_editor->clicked_axisview) {
3380 _editor->clicked_axisview->order_selection_trims (_item, false);
3382 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3386 Drag::start_grab (event, cursor);
3390 if (_operation == SelectionMove) {
3391 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3393 show_verbose_cursor_time (adjusted_current_frame (event));
3396 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3400 SelectionDrag::setup_pointer_frame_offset ()
3402 switch (_operation) {
3403 case CreateSelection:
3404 _pointer_frame_offset = 0;
3407 case SelectionStartTrim:
3409 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3412 case SelectionEndTrim:
3413 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3419 SelectionDrag::motion (GdkEvent* event, bool first_move)
3421 framepos_t start = 0;
3425 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3426 if (pending_time_axis.first == 0) {
3430 framepos_t const pending_position = adjusted_current_frame (event);
3432 /* only alter selection if things have changed */
3434 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3438 switch (_operation) {
3439 case CreateSelection:
3441 framepos_t grab = grab_frame ();
3444 _editor->snap_to (grab);
3447 if (pending_position < grab_frame()) {
3448 start = pending_position;
3451 end = pending_position;
3455 /* first drag: Either add to the selection
3456 or create a new selection
3462 /* adding to the selection */
3463 _editor->set_selected_track_as_side_effect (Selection::Add);
3464 //_editor->selection->add (_editor->clicked_axisview);
3465 _editor->clicked_selection = _editor->selection->add (start, end);
3470 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3471 //_editor->selection->set (_editor->clicked_axisview);
3472 _editor->set_selected_track_as_side_effect (Selection::Set);
3475 _editor->clicked_selection = _editor->selection->set (start, end);
3479 /* select the track that we're in */
3480 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3481 // _editor->set_selected_track_as_side_effect (Selection::Add);
3482 _editor->selection->add (pending_time_axis.first);
3483 _added_time_axes.push_back (pending_time_axis.first);
3486 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3487 tracks that we selected in the first place.
3490 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3491 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3493 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3494 while (i != _added_time_axes.end()) {
3496 list<TimeAxisView*>::iterator tmp = i;
3499 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3500 _editor->selection->remove (*i);
3501 _added_time_axes.remove (*i);
3510 case SelectionStartTrim:
3512 start = _editor->selection->time[_editor->clicked_selection].start;
3513 end = _editor->selection->time[_editor->clicked_selection].end;
3515 if (pending_position > end) {
3518 start = pending_position;
3522 case SelectionEndTrim:
3524 start = _editor->selection->time[_editor->clicked_selection].start;
3525 end = _editor->selection->time[_editor->clicked_selection].end;
3527 if (pending_position < start) {
3530 end = pending_position;
3537 start = _editor->selection->time[_editor->clicked_selection].start;
3538 end = _editor->selection->time[_editor->clicked_selection].end;
3540 length = end - start;
3542 start = pending_position;
3543 _editor->snap_to (start);
3545 end = start + length;
3550 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3551 _editor->start_canvas_autoscroll (1, 0);
3555 _editor->selection->replace (_editor->clicked_selection, start, end);
3558 if (_operation == SelectionMove) {
3559 show_verbose_cursor_time(start);
3561 show_verbose_cursor_time(pending_position);
3566 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3568 Session* s = _editor->session();
3570 if (movement_occurred) {
3571 motion (event, false);
3572 /* XXX this is not object-oriented programming at all. ick */
3573 if (_editor->selection->time.consolidate()) {
3574 _editor->selection->TimeChanged ();
3577 /* XXX what if its a music time selection? */
3579 if ((s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3580 s->request_play_range (&_editor->selection->time, true);
3582 if (Config->get_always_play_range()) {
3583 if (_editor->doing_range_stuff()) {
3584 s->request_locate (_editor->get_selection().time.start());
3591 /* just a click, no pointer movement.
3594 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3595 if (!_time_selection_at_start) {
3596 if (_editor->clicked_regionview) {
3597 if (_editor->get_selection().selected (_editor->clicked_regionview)) {
3598 /* range select the entire current
3601 _editor->select_range (_editor->get_selection().regions.start(),
3602 _editor->get_selection().regions.end_frame());
3604 /* range select this (unselected)
3607 _editor->select_range (_editor->clicked_regionview->region()->position(),
3608 _editor->clicked_regionview->region()->last_frame());
3612 _editor->selection->clear_time();
3616 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3617 _editor->selection->set (_editor->clicked_axisview);
3620 if (s && s->get_play_range () && s->transport_rolling()) {
3621 s->request_stop (false, false);
3624 if (Config->get_always_play_range()) {
3625 if (_editor->doing_range_stuff()) {
3626 s->request_locate (_editor->get_selection().time.start());
3631 _editor->stop_canvas_autoscroll ();
3635 SelectionDrag::aborted (bool)
3640 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3645 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3647 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3648 physical_screen_height (_editor->get_window()));
3649 _drag_rect->hide ();
3651 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3652 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3656 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3658 if (_editor->session() == 0) {
3662 Gdk::Cursor* cursor = 0;
3664 if (!_editor->temp_location) {
3665 _editor->temp_location = new Location (*_editor->session());
3668 switch (_operation) {
3669 case CreateRangeMarker:
3670 case CreateTransportMarker:
3671 case CreateCDMarker:
3673 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3678 cursor = _editor->cursors()->selector;
3682 Drag::start_grab (event, cursor);
3684 show_verbose_cursor_time (adjusted_current_frame (event));
3688 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3690 framepos_t start = 0;
3692 ArdourCanvas::SimpleRect *crect;
3694 switch (_operation) {
3695 case CreateRangeMarker:
3696 crect = _editor->range_bar_drag_rect;
3698 case CreateTransportMarker:
3699 crect = _editor->transport_bar_drag_rect;
3701 case CreateCDMarker:
3702 crect = _editor->cd_marker_bar_drag_rect;
3705 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3710 framepos_t const pf = adjusted_current_frame (event);
3712 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3713 framepos_t grab = grab_frame ();
3714 _editor->snap_to (grab);
3716 if (pf < grab_frame()) {
3724 /* first drag: Either add to the selection
3725 or create a new selection.
3730 _editor->temp_location->set (start, end);
3734 update_item (_editor->temp_location);
3736 //_drag_rect->raise_to_top();
3741 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3742 _editor->start_canvas_autoscroll (1, 0);
3746 _editor->temp_location->set (start, end);
3748 double x1 = _editor->frame_to_pixel (start);
3749 double x2 = _editor->frame_to_pixel (end);
3750 crect->property_x1() = x1;
3751 crect->property_x2() = x2;
3753 update_item (_editor->temp_location);
3756 show_verbose_cursor_time (pf);
3761 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3763 Location * newloc = 0;
3767 if (movement_occurred) {
3768 motion (event, false);
3771 switch (_operation) {
3772 case CreateRangeMarker:
3773 case CreateCDMarker:
3775 _editor->begin_reversible_command (_("new range marker"));
3776 XMLNode &before = _editor->session()->locations()->get_state();
3777 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3778 if (_operation == CreateCDMarker) {
3779 flags = Location::IsRangeMarker | Location::IsCDMarker;
3780 _editor->cd_marker_bar_drag_rect->hide();
3783 flags = Location::IsRangeMarker;
3784 _editor->range_bar_drag_rect->hide();
3786 newloc = new Location (
3787 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3790 _editor->session()->locations()->add (newloc, true);
3791 XMLNode &after = _editor->session()->locations()->get_state();
3792 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3793 _editor->commit_reversible_command ();
3797 case CreateTransportMarker:
3798 // popup menu to pick loop or punch
3799 _editor->new_transport_marker_context_menu (&event->button, _item);
3803 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3805 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3810 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3812 if (end == max_framepos) {
3813 end = _editor->session()->current_end_frame ();
3816 if (start == max_framepos) {
3817 start = _editor->session()->current_start_frame ();
3820 switch (_editor->mouse_mode) {
3822 /* find the two markers on either side and then make the selection from it */
3823 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3827 /* find the two markers on either side of the click and make the range out of it */
3828 _editor->selection->set (start, end);
3837 _editor->stop_canvas_autoscroll ();
3841 RangeMarkerBarDrag::aborted (bool)
3847 RangeMarkerBarDrag::update_item (Location* location)
3849 double const x1 = _editor->frame_to_pixel (location->start());
3850 double const x2 = _editor->frame_to_pixel (location->end());
3852 _drag_rect->property_x1() = x1;
3853 _drag_rect->property_x2() = x2;
3856 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3860 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3864 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3866 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3867 Drag::start_grab (event, _editor->cursors()->zoom_out);
3870 Drag::start_grab (event, _editor->cursors()->zoom_in);
3874 show_verbose_cursor_time (adjusted_current_frame (event));
3878 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3883 framepos_t const pf = adjusted_current_frame (event);
3885 framepos_t grab = grab_frame ();
3886 _editor->snap_to_with_modifier (grab, event);
3888 /* base start and end on initial click position */
3900 _editor->zoom_rect->show();
3901 _editor->zoom_rect->raise_to_top();
3904 _editor->reposition_zoom_rect(start, end);
3906 show_verbose_cursor_time (pf);
3911 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3913 if (movement_occurred) {
3914 motion (event, false);
3916 if (grab_frame() < last_pointer_frame()) {
3917 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
3919 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
3922 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3923 _editor->tav_zoom_step (_zoom_out);
3925 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3929 _editor->zoom_rect->hide();
3933 MouseZoomDrag::aborted (bool)
3935 _editor->zoom_rect->hide ();
3938 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3940 , _cumulative_dx (0)
3941 , _cumulative_dy (0)
3943 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3945 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3946 _region = &_primary->region_view ();
3947 _note_height = _region->midi_stream_view()->note_height ();
3951 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3953 Drag::start_grab (event);
3955 if (!(_was_selected = _primary->selected())) {
3957 /* tertiary-click means extend selection - we'll do that on button release,
3958 so don't add it here, because otherwise we make it hard to figure
3959 out the "extend-to" range.
3962 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3965 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3968 _region->note_selected (_primary, true);
3970 _region->unique_select (_primary);
3976 /** @return Current total drag x change in frames */
3978 NoteDrag::total_dx () const
3981 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3983 /* primary note time */
3984 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3986 /* new time of the primary note in session frames */
3987 frameoffset_t st = n + dx;
3989 framepos_t const rp = _region->region()->position ();
3991 /* prevent the note being dragged earlier than the region's position */
3994 /* snap and return corresponding delta */
3995 return _region->snap_frame_to_frame (st - rp) + rp - n;
3998 /** @return Current total drag y change in note number */
4000 NoteDrag::total_dy () const
4002 MidiStreamView* msv = _region->midi_stream_view ();
4003 double const y = _region->midi_view()->y_position ();
4004 /* new current note */
4005 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4007 n = max (msv->lowest_note(), n);
4008 n = min (msv->highest_note(), n);
4009 /* and work out delta */
4010 return n - msv->y_to_note (grab_y() - y);
4014 NoteDrag::motion (GdkEvent *, bool)
4016 /* Total change in x and y since the start of the drag */
4017 frameoffset_t const dx = total_dx ();
4018 int8_t const dy = total_dy ();
4020 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4021 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
4022 double const tdy = -dy * _note_height - _cumulative_dy;
4025 _cumulative_dx += tdx;
4026 _cumulative_dy += tdy;
4028 int8_t note_delta = total_dy();
4030 _region->move_selection (tdx, tdy, note_delta);
4032 /* the new note value may be the same as the old one, but we
4033 * don't know what that means because the selection may have
4034 * involved more than one note and we might be doing something
4035 * odd with them. so show the note value anyway, always.
4039 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4041 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4042 (int) floor (new_note));
4044 show_verbose_cursor_text (buf);
4049 NoteDrag::finished (GdkEvent* ev, bool moved)
4052 /* no motion - select note */
4054 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4055 _editor->current_mouse_mode() == Editing::MouseDraw) {
4057 if (_was_selected) {
4058 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4060 _region->note_deselected (_primary);
4063 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4064 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4066 if (!extend && !add && _region->selection_size() > 1) {
4067 _region->unique_select (_primary);
4068 } else if (extend) {
4069 _region->note_selected (_primary, true, true);
4071 /* it was added during button press */
4076 _region->note_dropped (_primary, total_dx(), total_dy());
4081 NoteDrag::aborted (bool)
4086 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4087 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4088 : Drag (editor, atv->base_item ())
4090 , _nothing_to_drag (false)
4092 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4094 setup (atv->lines ());
4097 /** Make an AutomationRangeDrag for region gain lines */
4098 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4099 : Drag (editor, rv->get_canvas_group ())
4101 , _nothing_to_drag (false)
4103 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4105 list<boost::shared_ptr<AutomationLine> > lines;
4106 lines.push_back (rv->get_gain_line ());
4110 /** @param lines AutomationLines to drag.
4111 * @param offset Offset from the session start to the points in the AutomationLines.
4114 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4116 /* find the lines that overlap the ranges being dragged */
4117 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4118 while (i != lines.end ()) {
4119 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4122 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4124 /* check this range against all the AudioRanges that we are using */
4125 list<AudioRange>::const_iterator k = _ranges.begin ();
4126 while (k != _ranges.end()) {
4127 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4133 /* add it to our list if it overlaps at all */
4134 if (k != _ranges.end()) {
4139 _lines.push_back (n);
4145 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4149 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4151 Drag::start_grab (event, cursor);
4153 /* Get line states before we start changing things */
4154 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4155 i->state = &i->line->get_state ();
4158 if (_ranges.empty()) {
4160 /* No selected time ranges: drag all points */
4161 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4162 uint32_t const N = i->line->npoints ();
4163 for (uint32_t j = 0; j < N; ++j) {
4164 i->points.push_back (i->line->nth (j));
4170 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4172 framecnt_t const half = (i->start + i->end) / 2;
4174 /* find the line that this audio range starts in */
4175 list<Line>::iterator j = _lines.begin();
4176 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4180 if (j != _lines.end()) {
4181 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4183 /* j is the line that this audio range starts in; fade into it;
4184 64 samples length plucked out of thin air.
4187 framepos_t a = i->start + 64;
4192 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4193 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4195 the_list->add (p, the_list->eval (p));
4196 the_list->add (q, the_list->eval (q));
4199 /* same thing for the end */
4202 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4206 if (j != _lines.end()) {
4207 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4209 /* j is the line that this audio range starts in; fade out of it;
4210 64 samples length plucked out of thin air.
4213 framepos_t b = i->end - 64;
4218 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4219 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4221 the_list->add (p, the_list->eval (p));
4222 the_list->add (q, the_list->eval (q));
4226 _nothing_to_drag = true;
4228 /* Find all the points that should be dragged and put them in the relevant
4229 points lists in the Line structs.
4232 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4234 uint32_t const N = i->line->npoints ();
4235 for (uint32_t j = 0; j < N; ++j) {
4237 /* here's a control point on this line */
4238 ControlPoint* p = i->line->nth (j);
4239 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4241 /* see if it's inside a range */
4242 list<AudioRange>::const_iterator k = _ranges.begin ();
4243 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4247 if (k != _ranges.end()) {
4248 /* dragging this point */
4249 _nothing_to_drag = false;
4250 i->points.push_back (p);
4256 if (_nothing_to_drag) {
4260 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4261 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4266 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4268 if (_nothing_to_drag) {
4272 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4273 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4275 /* we are ignoring x position for this drag, so we can just pass in anything */
4276 i->line->drag_motion (0, f, true, false);
4281 AutomationRangeDrag::finished (GdkEvent* event, bool)
4283 if (_nothing_to_drag) {
4287 motion (event, false);
4288 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4289 i->line->end_drag ();
4292 _editor->session()->commit_reversible_command ();
4296 AutomationRangeDrag::aborted (bool)
4298 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4303 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4306 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4307 layer = v->region()->layer ();
4308 initial_y = v->get_canvas_group()->property_y ();
4309 initial_playlist = v->region()->playlist ();
4310 initial_position = v->region()->position ();
4311 initial_end = v->region()->position () + v->region()->length ();
4314 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4318 , _cumulative_dx (0)
4320 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4321 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4326 PatchChangeDrag::motion (GdkEvent* ev, bool)
4328 framepos_t f = adjusted_current_frame (ev);
4329 boost::shared_ptr<Region> r = _region_view->region ();
4330 f = max (f, r->position ());
4331 f = min (f, r->last_frame ());
4333 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4334 double const dxu = _editor->frame_to_unit (dxf); // permitted fx in units
4335 _patch_change->move (dxu - _cumulative_dx, 0);
4336 _cumulative_dx = dxu;
4340 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4342 if (!movement_occurred) {
4346 boost::shared_ptr<Region> r (_region_view->region ());
4347 framepos_t f = adjusted_current_frame (ev);
4348 f = max (f, r->position ());
4349 f = min (f, r->last_frame ());
4351 _region_view->move_patch_change (
4353 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4358 PatchChangeDrag::aborted (bool)
4360 _patch_change->move (-_cumulative_dx, 0);
4364 PatchChangeDrag::setup_pointer_frame_offset ()
4366 boost::shared_ptr<Region> region = _region_view->region ();
4367 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4370 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4371 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4378 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4380 framepos_t const p = _region_view->region()->position ();
4381 double const y = _region_view->midi_view()->y_position ();
4383 x1 = max ((framepos_t) 0, x1 - p);
4384 x2 = max ((framepos_t) 0, x2 - p);
4385 y1 = max (0.0, y1 - y);
4386 y2 = max (0.0, y2 - y);
4388 _region_view->update_drag_selection (
4389 _editor->frame_to_pixel (x1),
4390 _editor->frame_to_pixel (x2),
4393 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4398 MidiRubberbandSelectDrag::deselect_things ()
4403 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4404 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4407 _vertical_only = true;
4411 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4413 double const y = _region_view->midi_view()->y_position ();
4415 y1 = max (0.0, y1 - y);
4416 y2 = max (0.0, y2 - y);
4418 _region_view->update_vertical_drag_selection (
4421 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4426 MidiVerticalSelectDrag::deselect_things ()
4431 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4432 : RubberbandSelectDrag (e, i)
4438 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4440 if (drag_in_progress) {
4441 /* We just want to select things at the end of the drag, not during it */
4445 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4447 _editor->begin_reversible_command (_("rubberband selection"));
4448 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4449 _editor->commit_reversible_command ();
4453 EditorRubberbandSelectDrag::deselect_things ()
4455 if (!getenv("ARDOUR_SAE")) {
4456 _editor->selection->clear_tracks();
4458 _editor->selection->clear_regions();
4459 _editor->selection->clear_points ();
4460 _editor->selection->clear_lines ();
4463 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4471 NoteCreateDrag::~NoteCreateDrag ()
4477 NoteCreateDrag::grid_frames (framepos_t t) const
4480 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4485 return _region_view->region_beats_to_region_frames (grid_beats);
4489 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4491 Drag::start_grab (event, cursor);
4493 _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4495 framepos_t pf = _drags->current_pointer_frame ();
4496 framecnt_t const g = grid_frames (pf);
4498 /* Hack so that we always snap to the note that we are over, instead of snapping
4499 to the next one if we're more than halfway through the one we're over.
4501 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4505 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4507 MidiStreamView* sv = _region_view->midi_stream_view ();
4508 double const x = _editor->frame_to_pixel (_note[0]);
4509 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4511 _drag_rect->property_x1() = x;
4512 _drag_rect->property_y1() = y;
4513 _drag_rect->property_x2() = x;
4514 _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4516 _drag_rect->property_outline_what() = 0xff;
4517 _drag_rect->property_outline_color_rgba() = 0xffffff99;
4518 _drag_rect->property_fill_color_rgba() = 0xffffff66;
4522 NoteCreateDrag::motion (GdkEvent* event, bool)
4524 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4525 double const x = _editor->frame_to_pixel (_note[1]);
4526 if (_note[1] > _note[0]) {
4527 _drag_rect->property_x2() = x;
4529 _drag_rect->property_x1() = x;
4534 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4536 if (!had_movement) {
4540 framepos_t const start = min (_note[0], _note[1]);
4541 framecnt_t length = abs (_note[0] - _note[1]);
4543 framecnt_t const g = grid_frames (start);
4544 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4546 if (_editor->snap_mode() == SnapNormal && length < g) {
4547 length = g - one_tick;
4550 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4552 _region_view->create_note_at (start, _drag_rect->property_y1(), length_beats, false);
4556 NoteCreateDrag::y_to_region (double y) const
4559 _region_view->get_canvas_group()->w2i (x, y);
4564 NoteCreateDrag::aborted (bool)
4569 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4577 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4579 Drag::start_grab (event, cursor);
4583 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4589 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4592 distance = _drags->current_pointer_x() - grab_x();
4593 len = ar->fade_in()->back()->when;
4595 distance = grab_x() - _drags->current_pointer_x();
4596 len = ar->fade_out()->back()->when;
4599 /* how long should it be ? */
4601 new_length = len + _editor->unit_to_frame (distance);
4603 /* now check with the region that this is legal */
4605 new_length = ar->verify_xfade_bounds (new_length, start);
4608 arv->redraw_start_xfade_to (ar, new_length);
4610 arv->redraw_end_xfade_to (ar, new_length);
4615 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
4621 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4624 distance = _drags->current_pointer_x() - grab_x();
4625 len = ar->fade_in()->back()->when;
4627 distance = grab_x() - _drags->current_pointer_x();
4628 len = ar->fade_out()->back()->when;
4631 new_length = ar->verify_xfade_bounds (len + _editor->unit_to_frame (distance), start);
4633 _editor->begin_reversible_command ("xfade trim");
4634 ar->playlist()->clear_owned_changes ();
4637 ar->set_fade_in_length (new_length);
4639 ar->set_fade_out_length (new_length);
4642 /* Adjusting the xfade may affect other regions in the playlist, so we need
4643 to get undo Commands from the whole playlist rather than just the
4647 vector<Command*> cmds;
4648 ar->playlist()->rdiff (cmds);
4649 _editor->session()->add_commands (cmds);
4650 _editor->commit_reversible_command ();
4655 CrossfadeEdgeDrag::aborted (bool)
4658 arv->redraw_start_xfade ();
4660 arv->redraw_end_xfade ();