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/audioengine.h"
33 #include "ardour/audioregion.h"
34 #include "ardour/dB.h"
35 #include "ardour/midi_region.h"
36 #include "ardour/operations.h"
37 #include "ardour/region_factory.h"
38 #include "ardour/session.h"
43 #include "audio_region_view.h"
44 #include "midi_region_view.h"
45 #include "ardour_ui.h"
46 #include "gui_thread.h"
47 #include "control_point.h"
49 #include "region_gain_line.h"
50 #include "editor_drag.h"
51 #include "audio_time_axis.h"
52 #include "midi_time_axis.h"
53 #include "canvas-note.h"
54 #include "selection.h"
55 #include "midi_selection.h"
56 #include "automation_time_axis.h"
58 #include "editor_cursors.h"
59 #include "mouse_cursors.h"
60 #include "verbose_cursor.h"
63 using namespace ARDOUR;
66 using namespace Gtkmm2ext;
67 using namespace Editing;
68 using namespace ArdourCanvas;
70 using Gtkmm2ext::Keyboard;
72 double ControlPointDrag::_zero_gain_fraction = -1.0;
74 DragManager::DragManager (Editor* e)
77 , _current_pointer_frame (0)
81 DragManager::~DragManager ()
86 /** Call abort for each active drag */
92 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
97 if (!_drags.empty ()) {
98 _editor->set_follow_playhead (_old_follow_playhead, false);
107 DragManager::add (Drag* d)
109 d->set_manager (this);
110 _drags.push_back (d);
114 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
116 d->set_manager (this);
117 _drags.push_back (d);
122 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
124 /* Prevent follow playhead during the drag to be nice to the user */
125 _old_follow_playhead = _editor->follow_playhead ();
126 _editor->set_follow_playhead (false);
128 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
130 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
131 (*i)->start_grab (e, c);
135 /** Call end_grab for each active drag.
136 * @return true if any drag reported movement having occurred.
139 DragManager::end_grab (GdkEvent* e)
144 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
145 bool const t = (*i)->end_grab (e);
156 _editor->set_follow_playhead (_old_follow_playhead, false);
162 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
166 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
168 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
169 bool const t = (*i)->motion_handler (e, from_autoscroll);
180 DragManager::have_item (ArdourCanvas::Item* i) const
182 list<Drag*>::const_iterator j = _drags.begin ();
183 while (j != _drags.end() && (*j)->item () != i) {
187 return j != _drags.end ();
190 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
193 , _pointer_frame_offset (0)
194 , _move_threshold_passed (false)
195 , _raw_grab_frame (0)
197 , _last_pointer_frame (0)
203 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
209 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, time);
211 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
216 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
218 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
220 if (Keyboard::is_button2_event (&event->button)) {
221 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
222 _y_constrained = true;
223 _x_constrained = false;
225 _y_constrained = false;
226 _x_constrained = true;
229 _x_constrained = false;
230 _y_constrained = false;
233 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
234 setup_pointer_frame_offset ();
235 _grab_frame = adjusted_frame (_raw_grab_frame, event);
236 _last_pointer_frame = _grab_frame;
237 _last_pointer_x = _grab_x;
238 _last_pointer_y = _grab_y;
241 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
244 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
249 if (_editor->session() && _editor->session()->transport_rolling()) {
252 _was_rolling = false;
255 switch (_editor->snap_type()) {
256 case SnapToRegionStart:
257 case SnapToRegionEnd:
258 case SnapToRegionSync:
259 case SnapToRegionBoundary:
260 _editor->build_region_boundary_cache ();
267 /** Call to end a drag `successfully'. Ungrabs item and calls
268 * subclass' finished() method.
270 * @param event GDK event, or 0.
271 * @return true if some movement occurred, otherwise false.
274 Drag::end_grab (GdkEvent* event)
276 _editor->stop_canvas_autoscroll ();
278 _item->ungrab (event ? event->button.time : 0);
280 finished (event, _move_threshold_passed);
282 _editor->verbose_cursor()->hide ();
284 return _move_threshold_passed;
288 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
292 if (f > _pointer_frame_offset) {
293 pos = f - _pointer_frame_offset;
297 _editor->snap_to_with_modifier (pos, event);
304 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
306 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
310 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
312 /* check to see if we have moved in any way that matters since the last motion event */
313 if (_move_threshold_passed &&
314 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
315 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
319 pair<framecnt_t, int> const threshold = move_threshold ();
321 bool const old_move_threshold_passed = _move_threshold_passed;
323 if (!from_autoscroll && !_move_threshold_passed) {
325 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
326 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
328 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
331 if (active (_editor->mouse_mode) && _move_threshold_passed) {
333 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
334 if (!from_autoscroll) {
335 bool const moving_left = _drags->current_pointer_x() < _last_pointer_x;
336 bool const moving_up = _drags->current_pointer_y() < _last_pointer_y;
337 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), moving_left, moving_up);
340 motion (event, _move_threshold_passed != old_move_threshold_passed);
342 _last_pointer_x = _drags->current_pointer_x ();
343 _last_pointer_y = _drags->current_pointer_y ();
344 _last_pointer_frame = adjusted_current_frame (event);
352 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
360 aborted (_move_threshold_passed);
362 _editor->stop_canvas_autoscroll ();
363 _editor->verbose_cursor()->hide ();
367 Drag::show_verbose_cursor_time (framepos_t frame)
369 _editor->verbose_cursor()->set_time (
371 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
372 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
375 _editor->verbose_cursor()->show ();
379 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
381 _editor->verbose_cursor()->show (xoffset);
383 _editor->verbose_cursor()->set_duration (
385 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
386 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
391 Drag::show_verbose_cursor_text (string const & text)
393 _editor->verbose_cursor()->show ();
395 _editor->verbose_cursor()->set (
397 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
398 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
402 boost::shared_ptr<Region>
403 Drag::add_midi_region (MidiTimeAxisView* view)
405 if (_editor->session()) {
406 const TempoMap& map (_editor->session()->tempo_map());
407 framecnt_t pos = grab_frame();
408 const Meter& m = map.meter_at (pos);
409 /* not that the frame rate used here can be affected by pull up/down which
412 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
413 return view->add_region (grab_frame(), len, true);
416 return boost::shared_ptr<Region>();
419 struct EditorOrderTimeAxisViewSorter {
420 bool operator() (TimeAxisView* a, TimeAxisView* b) {
421 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
422 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
424 return ra->route()->order_key (EditorSort) < rb->route()->order_key (EditorSort);
428 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
432 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
434 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
435 as some of the regions we are dragging may be on such tracks.
438 TrackViewList track_views = _editor->track_views;
439 track_views.sort (EditorOrderTimeAxisViewSorter ());
441 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
442 _time_axis_views.push_back (*i);
444 TimeAxisView::Children children_list = (*i)->get_child_list ();
445 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
446 _time_axis_views.push_back (j->get());
450 /* the list of views can be empty at this point if this is a region list-insert drag
453 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
454 _views.push_back (DraggingView (*i, this));
457 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
461 RegionDrag::region_going_away (RegionView* v)
463 list<DraggingView>::iterator i = _views.begin ();
464 while (i != _views.end() && i->view != v) {
468 if (i != _views.end()) {
473 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
474 * or -1 if it is not found.
477 RegionDrag::find_time_axis_view (TimeAxisView* t) const
480 int const N = _time_axis_views.size ();
481 while (i < N && _time_axis_views[i] != t) {
492 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
493 : RegionDrag (e, i, p, v),
502 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
504 Drag::start_grab (event, cursor);
506 show_verbose_cursor_time (_last_frame_position);
508 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
509 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
510 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
514 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
516 /* compute the amount of pointer motion in frames, and where
517 the region would be if we moved it by that much.
519 *pending_region_position = adjusted_current_frame (event);
521 framepos_t sync_frame;
522 framecnt_t sync_offset;
525 sync_offset = _primary->region()->sync_offset (sync_dir);
527 /* we don't handle a sync point that lies before zero.
529 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
531 sync_frame = *pending_region_position + (sync_dir*sync_offset);
533 _editor->snap_to_with_modifier (sync_frame, event);
535 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
538 *pending_region_position = _last_frame_position;
541 if (*pending_region_position > max_framepos - _primary->region()->length()) {
542 *pending_region_position = _last_frame_position;
547 /* in locked edit mode, reverse the usual meaning of _x_constrained */
548 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
550 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
552 /* x movement since last time (in pixels) */
553 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
555 /* total x movement */
556 framecnt_t total_dx = *pending_region_position;
557 if (regions_came_from_canvas()) {
558 total_dx = total_dx - grab_frame ();
561 /* check that no regions have gone off the start of the session */
562 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
563 if ((i->view->region()->position() + total_dx) < 0) {
565 *pending_region_position = _last_frame_position;
570 _last_frame_position = *pending_region_position;
577 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
579 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
580 int const n = i->time_axis_view + delta_track;
581 if (n < 0 || n >= int (_time_axis_views.size())) {
582 /* off the top or bottom track */
586 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
587 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
588 /* not a track, or the wrong type */
592 double const l = i->layer + delta_layer;
594 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
595 mode to allow the user to place a region below another on layer 0.
597 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
598 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
599 If it has, the layers will be munged later anyway, so it's ok.
605 /* all regions being dragged are ok with this change */
610 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
612 assert (!_views.empty ());
614 /* Find the TimeAxisView that the pointer is now over */
615 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
617 if (first_move && tv.first->view()->layer_display() == Stacked) {
618 tv.first->view()->set_layer_display (Expanded);
621 /* Bail early if we're not over a track */
622 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
623 if (!rtv || !rtv->is_track()) {
624 _editor->verbose_cursor()->hide ();
628 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
630 /* Here's the current pointer position in terms of time axis view and layer */
631 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
632 double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
634 /* Work out the change in x */
635 framepos_t pending_region_position;
636 double const x_delta = compute_x_delta (event, &pending_region_position);
638 /* Work out the change in y */
639 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
640 double delta_layer = current_pointer_layer - _last_pointer_layer;
642 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
643 /* this y movement is not allowed, so do no y movement this time */
644 delta_time_axis_view = 0;
648 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
649 /* haven't reached next snap point, and we're not switching
650 trackviews nor layers. nothing to do.
655 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
657 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
659 RegionView* rv = i->view;
661 if (rv->region()->locked()
662 #ifdef WITH_VIDEOTIMELINE
663 || rv->region()->video_locked()
673 /* Absolutely no idea why this is necessary, but it is; without
674 it, the region view disappears after the reparent.
676 _editor->update_canvas_now ();
678 /* Reparent to a non scrolling group so that we can keep the
679 region selection above all time axis views.
680 Reparenting means that we will have to move the region view
681 later, as the two parent groups have different coordinates.
684 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
686 rv->fake_set_opaque (true);
689 /* If we have moved tracks, we'll fudge the layer delta so that the
690 region gets moved back onto layer 0 on its new track; this avoids
691 confusion when dragging regions from non-zero layers onto different
694 double this_delta_layer = delta_layer;
695 if (delta_time_axis_view != 0) {
696 this_delta_layer = - i->layer;
699 /* The TimeAxisView that this region is now on */
700 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
702 /* Ensure it is moved from stacked -> expanded if appropriate */
703 if (tv->view()->layer_display() == Stacked) {
704 tv->view()->set_layer_display (Expanded);
707 /* We're only allowed to go -ve in layer on Expanded views */
708 if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
709 this_delta_layer = - i->layer;
713 rv->set_height (tv->view()->child_height ());
715 /* Update show/hidden status as the region view may have come from a hidden track,
716 or have moved to one.
719 rv->get_canvas_group()->hide ();
721 rv->get_canvas_group()->show ();
724 /* Update the DraggingView */
725 i->time_axis_view += delta_time_axis_view;
726 i->layer += this_delta_layer;
729 _editor->mouse_brush_insert_region (rv, pending_region_position);
734 /* Get the y coordinate of the top of the track that this region is now on */
735 tv->canvas_display()->i2w (x, y);
736 y += _editor->get_trackview_group_vertical_offset();
738 /* And adjust for the layer that it should be on */
739 StreamView* cv = tv->view ();
740 switch (cv->layer_display ()) {
744 y += (cv->layers() - i->layer - 1) * cv->child_height ();
747 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
751 /* Now move the region view */
752 rv->move (x_delta, y - rv->get_canvas_group()->property_y());
755 } /* foreach region */
757 _total_x_delta += x_delta;
760 _editor->cursor_group->raise_to_top();
763 if (x_delta != 0 && !_brushing) {
764 show_verbose_cursor_time (_last_frame_position);
767 _last_pointer_time_axis_view += delta_time_axis_view;
768 _last_pointer_layer += delta_layer;
772 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
774 if (_copy && first_move) {
776 /* duplicate the regionview(s) and region(s) */
778 list<DraggingView> new_regionviews;
780 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
782 RegionView* rv = i->view;
783 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
784 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
786 const boost::shared_ptr<const Region> original = rv->region();
787 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
788 region_copy->set_position (original->position());
792 boost::shared_ptr<AudioRegion> audioregion_copy
793 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
795 nrv = new AudioRegionView (*arv, audioregion_copy);
797 boost::shared_ptr<MidiRegion> midiregion_copy
798 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
799 nrv = new MidiRegionView (*mrv, midiregion_copy);
804 nrv->get_canvas_group()->show ();
805 new_regionviews.push_back (DraggingView (nrv, this));
807 /* swap _primary to the copy */
809 if (rv == _primary) {
813 /* ..and deselect the one we copied */
815 rv->set_selected (false);
818 if (!new_regionviews.empty()) {
820 /* reflect the fact that we are dragging the copies */
822 _views = new_regionviews;
824 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
827 sync the canvas to what we think is its current state
828 without it, the canvas seems to
829 "forget" to update properly after the upcoming reparent()
830 ..only if the mouse is in rapid motion at the time of the grab.
831 something to do with regionview creation taking so long?
833 _editor->update_canvas_now();
837 RegionMotionDrag::motion (event, first_move);
841 RegionMotionDrag::finished (GdkEvent *, bool)
843 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
848 if ((*i)->view()->layer_display() == Expanded) {
849 (*i)->view()->set_layer_display (Stacked);
855 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
857 RegionMotionDrag::finished (ev, movement_occurred);
859 if (!movement_occurred) {
864 /* reverse this here so that we have the correct logic to finalize
868 if (Config->get_edit_mode() == Lock) {
869 _x_constrained = !_x_constrained;
872 assert (!_views.empty ());
874 /* We might have hidden region views so that they weren't visible during the drag
875 (when they have been reparented). Now everything can be shown again, as region
876 views are back in their track parent groups.
878 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
879 i->view->get_canvas_group()->show ();
882 bool const changed_position = (_last_frame_position != _primary->region()->position());
883 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
884 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
886 _editor->update_canvas_now ();
906 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
910 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
912 RegionSelection new_views;
913 PlaylistSet modified_playlists;
914 list<RegionView*> views_to_delete;
917 /* all changes were made during motion event handlers */
919 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
923 _editor->commit_reversible_command ();
927 if (_x_constrained) {
928 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
930 _editor->begin_reversible_command (Operations::region_copy);
933 /* insert the regions into their new playlists */
934 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
936 if (i->view->region()->locked()
937 #ifdef WITH_VIDEOTIMELINE
938 || i->view->region()->video_locked()
946 if (changed_position && !_x_constrained) {
947 where = i->view->region()->position() - drag_delta;
949 where = i->view->region()->position();
952 RegionView* new_view = insert_region_into_playlist (
953 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
960 new_views.push_back (new_view);
962 /* we don't need the copied RegionView any more */
963 views_to_delete.push_back (i->view);
966 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
967 because when views are deleted they are automagically removed from _views, which messes
970 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
974 /* If we've created new regions either by copying or moving
975 to a new track, we want to replace the old selection with the new ones
978 if (new_views.size() > 0) {
979 _editor->selection->set (new_views);
982 /* write commands for the accumulated diffs for all our modified playlists */
983 add_stateful_diff_commands_for_playlists (modified_playlists);
985 _editor->commit_reversible_command ();
989 RegionMoveDrag::finished_no_copy (
990 bool const changed_position,
991 bool const changed_tracks,
992 framecnt_t const drag_delta
995 RegionSelection new_views;
996 PlaylistSet modified_playlists;
997 PlaylistSet frozen_playlists;
998 set<RouteTimeAxisView*> views_to_update;
1001 /* all changes were made during motion event handlers */
1002 _editor->commit_reversible_command ();
1006 if (_x_constrained) {
1007 _editor->begin_reversible_command (_("fixed time region drag"));
1009 _editor->begin_reversible_command (Operations::region_drag);
1012 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1014 RegionView* rv = i->view;
1016 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1017 double const dest_layer = i->layer;
1019 if (rv->region()->locked()
1020 #ifdef WITH_VIDEOTIMELINE
1021 || rv->region()->video_locked()
1028 views_to_update.insert (dest_rtv);
1032 if (changed_position && !_x_constrained) {
1033 where = rv->region()->position() - drag_delta;
1035 where = rv->region()->position();
1038 if (changed_tracks) {
1040 /* insert into new playlist */
1042 RegionView* new_view = insert_region_into_playlist (
1043 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1046 if (new_view == 0) {
1051 new_views.push_back (new_view);
1053 /* remove from old playlist */
1055 /* the region that used to be in the old playlist is not
1056 moved to the new one - we use a copy of it. as a result,
1057 any existing editor for the region should no longer be
1060 rv->hide_region_editor();
1061 rv->fake_set_opaque (false);
1063 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1067 rv->region()->clear_changes ();
1070 motion on the same track. plonk the previously reparented region
1071 back to its original canvas group (its streamview).
1072 No need to do anything for copies as they are fake regions which will be deleted.
1075 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1076 rv->get_canvas_group()->property_y() = i->initial_y;
1079 /* just change the model */
1081 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1083 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1084 playlist->set_layer (rv->region(), dest_layer);
1087 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1089 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1092 playlist->freeze ();
1095 /* this movement may result in a crossfade being modified, so we need to get undo
1096 data from the playlist as well as the region.
1099 r = modified_playlists.insert (playlist);
1101 playlist->clear_changes ();
1104 rv->region()->set_position (where);
1106 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1109 if (changed_tracks) {
1111 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1112 was selected in all of them, then removing it from a playlist will have removed all
1113 trace of it from _views (i.e. there were N regions selected, we removed 1,
1114 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1115 corresponding regionview, and _views is now empty).
1117 This could have invalidated any and all iterators into _views.
1119 The heuristic we use here is: if the region selection is empty, break out of the loop
1120 here. if the region selection is not empty, then restart the loop because we know that
1121 we must have removed at least the region(view) we've just been working on as well as any
1122 that we processed on previous iterations.
1124 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1125 we can just iterate.
1129 if (_views.empty()) {
1140 /* If we've created new regions either by copying or moving
1141 to a new track, we want to replace the old selection with the new ones
1144 if (new_views.size() > 0) {
1145 _editor->selection->set (new_views);
1148 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1152 /* write commands for the accumulated diffs for all our modified playlists */
1153 add_stateful_diff_commands_for_playlists (modified_playlists);
1155 _editor->commit_reversible_command ();
1157 /* We have futzed with the layering of canvas items on our streamviews.
1158 If any region changed layer, this will have resulted in the stream
1159 views being asked to set up their region views, and all will be well.
1160 If not, we might now have badly-ordered region views. Ask the StreamViews
1161 involved to sort themselves out, just in case.
1164 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1165 (*i)->view()->playlist_layered ((*i)->track ());
1169 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1170 * @param region Region to remove.
1171 * @param playlist playlist To remove from.
1172 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1173 * that clear_changes () is only called once per playlist.
1176 RegionMoveDrag::remove_region_from_playlist (
1177 boost::shared_ptr<Region> region,
1178 boost::shared_ptr<Playlist> playlist,
1179 PlaylistSet& modified_playlists
1182 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1185 playlist->clear_changes ();
1188 playlist->remove_region (region);
1192 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1193 * clearing the playlist's diff history first if necessary.
1194 * @param region Region to insert.
1195 * @param dest_rtv Destination RouteTimeAxisView.
1196 * @param dest_layer Destination layer.
1197 * @param where Destination position.
1198 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1199 * that clear_changes () is only called once per playlist.
1200 * @return New RegionView, or 0 if no insert was performed.
1203 RegionMoveDrag::insert_region_into_playlist (
1204 boost::shared_ptr<Region> region,
1205 RouteTimeAxisView* dest_rtv,
1208 PlaylistSet& modified_playlists
1211 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1212 if (!dest_playlist) {
1216 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1217 _new_region_view = 0;
1218 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1220 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1221 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1223 dest_playlist->clear_changes ();
1226 dest_playlist->add_region (region, where);
1228 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1229 dest_playlist->set_layer (region, dest_layer);
1234 assert (_new_region_view);
1236 return _new_region_view;
1240 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1242 _new_region_view = rv;
1246 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1248 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1249 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1251 _editor->session()->add_command (c);
1260 RegionMoveDrag::aborted (bool movement_occurred)
1264 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1271 RegionMotionDrag::aborted (movement_occurred);
1276 RegionMotionDrag::aborted (bool)
1278 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1279 if ((*i)->view()->layer_display() == Expanded) {
1280 (*i)->view()->set_layer_display (Stacked);
1284 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1285 RegionView* rv = i->view;
1286 TimeAxisView* tv = &(rv->get_time_axis_view ());
1287 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1289 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1290 rv->get_canvas_group()->property_y() = 0;
1292 rv->fake_set_opaque (false);
1293 rv->move (-_total_x_delta, 0);
1294 rv->set_height (rtv->view()->child_height ());
1297 _editor->update_canvas_now ();
1300 /** @param b true to brush, otherwise false.
1301 * @param c true to make copies of the regions being moved, otherwise false.
1303 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1304 : RegionMotionDrag (e, i, p, v, b),
1307 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1310 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1311 if (rtv && rtv->is_track()) {
1312 speed = rtv->track()->speed ();
1315 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1319 RegionMoveDrag::setup_pointer_frame_offset ()
1321 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1324 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1325 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1327 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1329 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1330 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1332 _primary = v->view()->create_region_view (r, false, false);
1334 _primary->get_canvas_group()->show ();
1335 _primary->set_position (pos, 0);
1336 _views.push_back (DraggingView (_primary, this));
1338 _last_frame_position = pos;
1340 _item = _primary->get_canvas_group ();
1344 RegionInsertDrag::finished (GdkEvent *, bool)
1346 _editor->update_canvas_now ();
1348 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1350 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1351 _primary->get_canvas_group()->property_y() = 0;
1353 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1355 _editor->begin_reversible_command (Operations::insert_region);
1356 playlist->clear_changes ();
1357 playlist->add_region (_primary->region (), _last_frame_position);
1358 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1359 _editor->commit_reversible_command ();
1367 RegionInsertDrag::aborted (bool)
1374 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1375 : RegionMoveDrag (e, i, p, v, false, false)
1377 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1380 struct RegionSelectionByPosition {
1381 bool operator() (RegionView*a, RegionView* b) {
1382 return a->region()->position () < b->region()->position();
1387 RegionSpliceDrag::motion (GdkEvent* event, bool)
1389 /* Which trackview is this ? */
1391 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1392 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1394 /* The region motion is only processed if the pointer is over
1398 if (!tv || !tv->is_track()) {
1399 /* To make sure we hide the verbose canvas cursor when the mouse is
1400 not held over and audiotrack.
1402 _editor->verbose_cursor()->hide ();
1408 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1414 RegionSelection copy (_editor->selection->regions);
1416 RegionSelectionByPosition cmp;
1419 framepos_t const pf = adjusted_current_frame (event);
1421 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1423 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1429 boost::shared_ptr<Playlist> playlist;
1431 if ((playlist = atv->playlist()) == 0) {
1435 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1440 if (pf < (*i)->region()->last_frame() + 1) {
1444 if (pf > (*i)->region()->first_frame()) {
1450 playlist->shuffle ((*i)->region(), dir);
1455 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1457 RegionMoveDrag::finished (event, movement_occurred);
1461 RegionSpliceDrag::aborted (bool)
1466 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1468 _view (dynamic_cast<MidiTimeAxisView*> (v))
1470 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1476 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1479 _region = add_midi_region (_view);
1480 _view->playlist()->freeze ();
1483 framepos_t const f = adjusted_current_frame (event);
1484 if (f < grab_frame()) {
1485 _region->set_position (f);
1488 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1489 so that if this region is duplicated, its duplicate starts on
1490 a snap point rather than 1 frame after a snap point. Otherwise things get
1491 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1492 place snapped notes at the start of the region.
1495 framecnt_t const len = (framecnt_t) fabs (f - grab_frame () - 1);
1496 _region->set_length (len < 1 ? 1 : len);
1502 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1504 if (!movement_occurred) {
1505 add_midi_region (_view);
1507 _view->playlist()->thaw ();
1512 RegionCreateDrag::aborted (bool)
1515 _view->playlist()->thaw ();
1521 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1525 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1529 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1531 Gdk::Cursor* cursor;
1532 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1533 float x_fraction = cnote->mouse_x_fraction ();
1535 if (x_fraction > 0.0 && x_fraction < 0.25) {
1536 cursor = _editor->cursors()->left_side_trim;
1538 cursor = _editor->cursors()->right_side_trim;
1541 Drag::start_grab (event, cursor);
1543 region = &cnote->region_view();
1545 double const region_start = region->get_position_pixels();
1546 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1548 if (grab_x() <= middle_point) {
1549 cursor = _editor->cursors()->left_side_trim;
1552 cursor = _editor->cursors()->right_side_trim;
1556 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1558 if (event->motion.state & Keyboard::PrimaryModifier) {
1564 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1566 if (ms.size() > 1) {
1567 /* has to be relative, may make no sense otherwise */
1571 /* select this note; if it is already selected, preserve the existing selection,
1572 otherwise make this note the only one selected.
1574 region->note_selected (cnote, cnote->selected ());
1576 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1577 MidiRegionSelection::iterator next;
1580 (*r)->begin_resizing (at_front);
1586 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1588 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1589 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1590 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1595 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1597 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1598 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1599 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1604 NoteResizeDrag::aborted (bool)
1606 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1607 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1608 (*r)->abort_resizing ();
1612 #ifdef WITH_VIDEOTIMELINE
1614 AVDraggingView::AVDraggingView (RegionView* v)
1617 initial_position = v->region()->position ();
1620 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1623 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1625 /* create a list of regions to move along */
1626 #if 1 /* all reagions -- with video_locked() */
1628 TrackViewList empty;
1630 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1631 std::list<RegionView*> views = rs.by_layer();
1632 #else /* selected regions -- with video_locked() */
1633 std::list<RegionView*> views = _editor->selection->regions.by_layer();
1635 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1636 RegionView* rv = (*i);
1637 if (!rv->region()->video_locked()) {
1640 _views.push_back (AVDraggingView (rv));
1645 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1647 Drag::start_grab (event);
1648 if (_editor->session() == 0) {
1652 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1653 _max_backwards_drag = (
1654 ARDOUR_UI::instance()->video_timeline->get_duration()
1655 + ARDOUR_UI::instance()->video_timeline->get_offset()
1656 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1659 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1660 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1661 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1664 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1667 Timecode::Time timecode;
1668 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1669 snprintf (buf, sizeof (buf), "Video Start:\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, (_startdrag_video_offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1670 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1671 _editor->verbose_cursor()->show ();
1675 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1677 if (_editor->session() == 0) {
1680 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1684 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1685 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(dt);
1687 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1688 dt = - _max_backwards_drag;
1691 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1692 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1694 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1695 RegionView* rv = i->view;
1696 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1699 _editor->update_canvas_now ();
1700 rv->fake_set_opaque (true);
1701 rv->region()->clear_changes ();
1702 rv->region()->suspend_property_changes();
1704 rv->region()->set_position(i->initial_position + dt);
1705 rv->region_changed(ARDOUR::Properties::position);
1708 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1709 Timecode::Time timecode;
1710 Timecode::Time timediff;
1712 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1713 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1714 snprintf (buf, sizeof (buf),
1715 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1716 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1717 , _("Video Start:"),
1718 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1720 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1722 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1723 _editor->verbose_cursor()->show ();
1727 VideoTimeLineDrag::finished (GdkEvent *event, bool movement_occurred)
1729 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1733 if (!movement_occurred || ! _editor->session()) {
1737 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1739 _editor->begin_reversible_command (_("Move Video"));
1741 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1742 ARDOUR_UI::instance()->video_timeline->save_undo();
1743 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1744 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1746 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1747 i->view->drag_end();
1748 i->view->fake_set_opaque (false);
1749 i->view->region()->resume_property_changes ();
1751 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1754 _editor->commit_reversible_command ();
1755 _editor->update_canvas_now ();
1759 VideoTimeLineDrag::aborted (bool)
1761 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1764 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1765 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1767 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1768 i->view->region()->resume_property_changes ();
1769 i->view->region()->set_position(i->initial_position);
1774 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1775 : RegionDrag (e, i, p, v)
1777 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1778 _preserve_fade_anchor = preserve_fade_anchor;
1782 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1785 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1786 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1788 if (tv && tv->is_track()) {
1789 speed = tv->track()->speed();
1792 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1793 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1794 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1796 framepos_t const pf = adjusted_current_frame (event);
1798 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1799 /* Move the contents of the region around without changing the region bounds */
1800 _operation = ContentsTrim;
1801 Drag::start_grab (event, _editor->cursors()->trimmer);
1803 /* These will get overridden for a point trim.*/
1804 if (pf < (region_start + region_length/2)) {
1805 /* closer to front */
1806 _operation = StartTrim;
1807 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1810 _operation = EndTrim;
1811 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1815 switch (_operation) {
1817 show_verbose_cursor_time (region_start);
1818 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1819 i->view->trim_front_starting ();
1823 show_verbose_cursor_time (region_end);
1826 show_verbose_cursor_time (pf);
1830 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1831 i->view->region()->suspend_property_changes ();
1836 TrimDrag::motion (GdkEvent* event, bool first_move)
1838 RegionView* rv = _primary;
1841 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1842 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1843 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1845 if (tv && tv->is_track()) {
1846 speed = tv->track()->speed();
1849 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1855 switch (_operation) {
1857 trim_type = "Region start trim";
1860 trim_type = "Region end trim";
1863 trim_type = "Region content trim";
1867 _editor->begin_reversible_command (trim_type);
1869 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1870 RegionView* rv = i->view;
1871 rv->fake_set_opaque (false);
1872 rv->enable_display (false);
1873 rv->region()->playlist()->clear_owned_changes ();
1875 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1878 arv->temporarily_hide_envelope ();
1882 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1883 insert_result = _editor->motion_frozen_playlists.insert (pl);
1885 if (insert_result.second) {
1891 bool non_overlap_trim = false;
1893 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1894 non_overlap_trim = true;
1897 switch (_operation) {
1899 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1900 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1901 if (changed && _preserve_fade_anchor) {
1902 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1907 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1908 distance = _drags->current_pointer_x() - grab_x();
1909 len = ar->fade_in()->back()->when;
1910 new_length = len - _editor->unit_to_frame (distance);
1911 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
1912 arv->reset_fade_in_shape_width (ar, new_length); //the grey shape
1919 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1920 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1921 if (changed && _preserve_fade_anchor) {
1922 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1927 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1928 distance = grab_x() - _drags->current_pointer_x();
1929 len = ar->fade_out()->back()->when;
1930 new_length = len - _editor->unit_to_frame (distance);
1931 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
1932 arv->reset_fade_out_shape_width (ar, new_length); //the grey shape
1940 bool swap_direction = false;
1942 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1943 swap_direction = true;
1946 framecnt_t frame_delta = 0;
1948 bool left_direction = false;
1949 if (last_pointer_frame() > adjusted_current_frame(event)) {
1950 left_direction = true;
1953 if (left_direction) {
1954 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1956 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1959 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1960 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1966 switch (_operation) {
1968 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1971 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1974 show_verbose_cursor_time (adjusted_current_frame (event));
1981 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1983 if (movement_occurred) {
1984 motion (event, false);
1986 if (_operation == StartTrim) {
1987 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1989 /* This must happen before the region's StatefulDiffCommand is created, as it may
1990 `correct' (ahem) the region's _start from being negative to being zero. It
1991 needs to be zero in the undo record.
1993 i->view->trim_front_ending ();
1995 if (_preserve_fade_anchor) {
1996 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2001 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2002 distance = _drags->current_pointer_x() - grab_x();
2003 len = ar->fade_in()->back()->when;
2004 new_length = len - _editor->unit_to_frame (distance);
2005 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2006 ar->set_fade_in_length(new_length);
2010 } else if (_operation == EndTrim) {
2011 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2012 if (_preserve_fade_anchor) {
2013 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2018 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2019 distance = _drags->current_pointer_x() - grab_x();
2020 len = ar->fade_out()->back()->when;
2021 new_length = len - _editor->unit_to_frame (distance);
2022 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2023 ar->set_fade_out_length(new_length);
2029 if (!_views.empty()) {
2030 if (_operation == StartTrim) {
2031 _editor->maybe_locate_with_edit_preroll(
2032 _views.begin()->view->region()->position());
2034 if (_operation == EndTrim) {
2035 _editor->maybe_locate_with_edit_preroll(
2036 _views.begin()->view->region()->position() +
2037 _views.begin()->view->region()->length());
2041 if (!_editor->selection->selected (_primary)) {
2042 _primary->thaw_after_trim ();
2045 set<boost::shared_ptr<Playlist> > diffed_playlists;
2047 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2048 i->view->thaw_after_trim ();
2049 i->view->enable_display (true);
2050 i->view->fake_set_opaque (true);
2052 /* Trimming one region may affect others on the playlist, so we need
2053 to get undo Commands from the whole playlist rather than just the
2054 region. Use diffed_playlists to make sure we don't diff a given
2055 playlist more than once.
2057 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2058 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2059 vector<Command*> cmds;
2061 _editor->session()->add_commands (cmds);
2062 diffed_playlists.insert (p);
2067 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2071 _editor->motion_frozen_playlists.clear ();
2072 _editor->commit_reversible_command();
2075 /* no mouse movement */
2076 _editor->point_trim (event, adjusted_current_frame (event));
2079 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2080 if (_operation == StartTrim) {
2081 i->view->trim_front_ending ();
2084 i->view->region()->resume_property_changes ();
2089 TrimDrag::aborted (bool movement_occurred)
2091 /* Our motion method is changing model state, so use the Undo system
2092 to cancel. Perhaps not ideal, as this will leave an Undo point
2093 behind which may be slightly odd from the user's point of view.
2098 if (movement_occurred) {
2102 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2103 i->view->region()->resume_property_changes ();
2108 TrimDrag::setup_pointer_frame_offset ()
2110 list<DraggingView>::iterator i = _views.begin ();
2111 while (i != _views.end() && i->view != _primary) {
2115 if (i == _views.end()) {
2119 switch (_operation) {
2121 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2124 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2131 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2135 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2136 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2141 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2143 Drag::start_grab (event, cursor);
2144 show_verbose_cursor_time (adjusted_current_frame(event));
2148 MeterMarkerDrag::setup_pointer_frame_offset ()
2150 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2154 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2158 // create a dummy marker for visual representation of moving the
2159 // section, because whether its a copy or not, we're going to
2160 // leave or lose the original marker (leave if its a copy; lose if its
2161 // not, because we'll remove it from the map).
2163 MeterSection section (_marker->meter());
2165 if (!section.movable()) {
2170 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2172 _marker = new MeterMarker (
2174 *_editor->meter_group,
2175 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
2177 *new MeterSection (_marker->meter())
2180 /* use the new marker for the grab */
2181 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2184 TempoMap& map (_editor->session()->tempo_map());
2185 /* get current state */
2186 before_state = &map.get_state();
2187 /* remove the section while we drag it */
2188 map.remove_meter (section, true);
2192 framepos_t const pf = adjusted_current_frame (event);
2193 _marker->set_position (pf);
2194 show_verbose_cursor_time (pf);
2198 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2200 if (!movement_occurred) {
2204 motion (event, false);
2206 Timecode::BBT_Time when;
2208 TempoMap& map (_editor->session()->tempo_map());
2209 map.bbt_time (last_pointer_frame(), when);
2211 if (_copy == true) {
2212 _editor->begin_reversible_command (_("copy meter mark"));
2213 XMLNode &before = map.get_state();
2214 map.add_meter (_marker->meter(), when);
2215 XMLNode &after = map.get_state();
2216 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2217 _editor->commit_reversible_command ();
2220 _editor->begin_reversible_command (_("move meter mark"));
2222 /* we removed it before, so add it back now */
2224 map.add_meter (_marker->meter(), when);
2225 XMLNode &after = map.get_state();
2226 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2227 _editor->commit_reversible_command ();
2230 // delete the dummy marker we used for visual representation while moving.
2231 // a new visual marker will show up automatically.
2236 MeterMarkerDrag::aborted (bool moved)
2238 _marker->set_position (_marker->meter().frame ());
2241 TempoMap& map (_editor->session()->tempo_map());
2242 /* we removed it before, so add it back now */
2243 map.add_meter (_marker->meter(), _marker->meter().frame());
2244 // delete the dummy marker we used for visual representation while moving.
2245 // a new visual marker will show up automatically.
2250 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2254 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2256 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2261 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2263 Drag::start_grab (event, cursor);
2264 show_verbose_cursor_time (adjusted_current_frame (event));
2268 TempoMarkerDrag::setup_pointer_frame_offset ()
2270 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2274 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2278 // create a dummy marker for visual representation of moving the
2279 // section, because whether its a copy or not, we're going to
2280 // leave or lose the original marker (leave if its a copy; lose if its
2281 // not, because we'll remove it from the map).
2283 // create a dummy marker for visual representation of moving the copy.
2284 // The actual copying is not done before we reach the finish callback.
2287 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2289 TempoSection section (_marker->tempo());
2291 _marker = new TempoMarker (
2293 *_editor->tempo_group,
2294 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
2296 *new TempoSection (_marker->tempo())
2299 /* use the new marker for the grab */
2300 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2303 TempoMap& map (_editor->session()->tempo_map());
2304 /* get current state */
2305 before_state = &map.get_state();
2306 /* remove the section while we drag it */
2307 map.remove_tempo (section, true);
2311 framepos_t const pf = adjusted_current_frame (event);
2312 _marker->set_position (pf);
2313 show_verbose_cursor_time (pf);
2317 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2319 if (!movement_occurred) {
2323 motion (event, false);
2325 TempoMap& map (_editor->session()->tempo_map());
2326 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2327 Timecode::BBT_Time when;
2329 map.bbt_time (beat_time, when);
2331 if (_copy == true) {
2332 _editor->begin_reversible_command (_("copy tempo mark"));
2333 XMLNode &before = map.get_state();
2334 map.add_tempo (_marker->tempo(), when);
2335 XMLNode &after = map.get_state();
2336 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2337 _editor->commit_reversible_command ();
2340 _editor->begin_reversible_command (_("move tempo mark"));
2341 /* we removed it before, so add it back now */
2342 map.add_tempo (_marker->tempo(), when);
2343 XMLNode &after = map.get_state();
2344 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2345 _editor->commit_reversible_command ();
2348 // delete the dummy marker we used for visual representation while moving.
2349 // a new visual marker will show up automatically.
2354 TempoMarkerDrag::aborted (bool moved)
2356 _marker->set_position (_marker->tempo().frame());
2358 TempoMap& map (_editor->session()->tempo_map());
2359 /* we removed it before, so add it back now */
2360 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2361 // delete the dummy marker we used for visual representation while moving.
2362 // a new visual marker will show up automatically.
2367 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2371 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2374 /** Do all the things we do when dragging the playhead to make it look as though
2375 * we have located, without actually doing the locate (because that would cause
2376 * the diskstream buffers to be refilled, which is too slow).
2379 CursorDrag::fake_locate (framepos_t t)
2381 _editor->playhead_cursor->set_position (t);
2383 Session* s = _editor->session ();
2384 if (s->timecode_transmission_suspended ()) {
2385 framepos_t const f = _editor->playhead_cursor->current_frame;
2386 s->send_mmc_locate (f);
2387 s->send_full_time_code (f);
2390 show_verbose_cursor_time (t);
2391 _editor->UpdateAllTransportClocks (t);
2395 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2397 Drag::start_grab (event, c);
2399 _grab_zoom = _editor->frames_per_unit;
2401 framepos_t where = _editor->event_frame (event, 0, 0);
2402 _editor->snap_to_with_modifier (where, event);
2404 _editor->_dragging_playhead = true;
2406 Session* s = _editor->session ();
2409 if (_was_rolling && _stop) {
2413 if (s->is_auditioning()) {
2414 s->cancel_audition ();
2418 if (AudioEngine::instance()->connected()) {
2420 /* do this only if we're the engine is connected
2421 * because otherwise this request will never be
2422 * serviced and we'll busy wait forever. likewise,
2423 * notice if we are disconnected while waiting for the
2424 * request to be serviced.
2427 s->request_suspend_timecode_transmission ();
2428 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2429 /* twiddle our thumbs */
2434 fake_locate (where);
2438 CursorDrag::motion (GdkEvent* event, bool)
2440 framepos_t const adjusted_frame = adjusted_current_frame (event);
2441 if (adjusted_frame != last_pointer_frame()) {
2442 fake_locate (adjusted_frame);
2444 _editor->update_canvas_now ();
2450 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2452 _editor->_dragging_playhead = false;
2454 if (!movement_occurred && _stop) {
2458 motion (event, false);
2460 Session* s = _editor->session ();
2462 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2463 _editor->_pending_locate_request = true;
2464 s->request_resume_timecode_transmission ();
2469 CursorDrag::aborted (bool)
2471 if (_editor->_dragging_playhead) {
2472 _editor->session()->request_resume_timecode_transmission ();
2473 _editor->_dragging_playhead = false;
2476 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2479 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2480 : RegionDrag (e, i, p, v)
2482 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2486 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2488 Drag::start_grab (event, cursor);
2490 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2491 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2493 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2497 FadeInDrag::setup_pointer_frame_offset ()
2499 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2500 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2501 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2505 FadeInDrag::motion (GdkEvent* event, bool)
2507 framecnt_t fade_length;
2509 framepos_t const pos = adjusted_current_frame (event);
2511 boost::shared_ptr<Region> region = _primary->region ();
2513 if (pos < (region->position() + 64)) {
2514 fade_length = 64; // this should be a minimum defined somewhere
2515 } else if (pos > region->last_frame()) {
2516 fade_length = region->length();
2518 fade_length = pos - region->position();
2521 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2523 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2529 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2532 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2536 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2538 if (!movement_occurred) {
2542 framecnt_t fade_length;
2544 framepos_t const pos = adjusted_current_frame (event);
2546 boost::shared_ptr<Region> region = _primary->region ();
2548 if (pos < (region->position() + 64)) {
2549 fade_length = 64; // this should be a minimum defined somewhere
2550 } else if (pos > region->last_frame()) {
2551 fade_length = region->length();
2553 fade_length = pos - region->position();
2556 _editor->begin_reversible_command (_("change fade in length"));
2558 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2560 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2566 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2567 XMLNode &before = alist->get_state();
2569 tmp->audio_region()->set_fade_in_length (fade_length);
2570 tmp->audio_region()->set_fade_in_active (true);
2572 XMLNode &after = alist->get_state();
2573 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2576 _editor->commit_reversible_command ();
2580 FadeInDrag::aborted (bool)
2582 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2583 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2589 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2593 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2594 : RegionDrag (e, i, p, v)
2596 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2600 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2602 Drag::start_grab (event, cursor);
2604 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2605 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2607 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2611 FadeOutDrag::setup_pointer_frame_offset ()
2613 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2614 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2615 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2619 FadeOutDrag::motion (GdkEvent* event, bool)
2621 framecnt_t fade_length;
2623 framepos_t const pos = adjusted_current_frame (event);
2625 boost::shared_ptr<Region> region = _primary->region ();
2627 if (pos > (region->last_frame() - 64)) {
2628 fade_length = 64; // this should really be a minimum fade defined somewhere
2630 else if (pos < region->position()) {
2631 fade_length = region->length();
2634 fade_length = region->last_frame() - pos;
2637 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2639 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2645 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2648 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2652 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2654 if (!movement_occurred) {
2658 framecnt_t fade_length;
2660 framepos_t const pos = adjusted_current_frame (event);
2662 boost::shared_ptr<Region> region = _primary->region ();
2664 if (pos > (region->last_frame() - 64)) {
2665 fade_length = 64; // this should really be a minimum fade defined somewhere
2667 else if (pos < region->position()) {
2668 fade_length = region->length();
2671 fade_length = region->last_frame() - pos;
2674 _editor->begin_reversible_command (_("change fade out length"));
2676 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2678 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2684 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2685 XMLNode &before = alist->get_state();
2687 tmp->audio_region()->set_fade_out_length (fade_length);
2688 tmp->audio_region()->set_fade_out_active (true);
2690 XMLNode &after = alist->get_state();
2691 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2694 _editor->commit_reversible_command ();
2698 FadeOutDrag::aborted (bool)
2700 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2701 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2707 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2711 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2714 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2716 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2719 _points.push_back (Gnome::Art::Point (0, 0));
2720 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2723 MarkerDrag::~MarkerDrag ()
2725 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2730 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2732 location = new Location (*l);
2733 markers.push_back (m);
2738 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2740 Drag::start_grab (event, cursor);
2744 Location *location = _editor->find_location_from_marker (_marker, is_start);
2745 _editor->_dragging_edit_point = true;
2747 update_item (location);
2749 // _drag_line->show();
2750 // _line->raise_to_top();
2753 show_verbose_cursor_time (location->start());
2755 show_verbose_cursor_time (location->end());
2758 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2761 case Selection::Toggle:
2762 /* we toggle on the button release */
2764 case Selection::Set:
2765 if (!_editor->selection->selected (_marker)) {
2766 _editor->selection->set (_marker);
2769 case Selection::Extend:
2771 Locations::LocationList ll;
2772 list<Marker*> to_add;
2774 _editor->selection->markers.range (s, e);
2775 s = min (_marker->position(), s);
2776 e = max (_marker->position(), e);
2779 if (e < max_framepos) {
2782 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2783 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2784 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2787 to_add.push_back (lm->start);
2790 to_add.push_back (lm->end);
2794 if (!to_add.empty()) {
2795 _editor->selection->add (to_add);
2799 case Selection::Add:
2800 _editor->selection->add (_marker);
2804 /* Set up copies for us to manipulate during the drag
2807 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2809 Location* l = _editor->find_location_from_marker (*i, is_start);
2816 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2818 /* range: check that the other end of the range isn't
2821 CopiedLocationInfo::iterator x;
2822 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2823 if (*(*x).location == *l) {
2827 if (x == _copied_locations.end()) {
2828 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2830 (*x).markers.push_back (*i);
2831 (*x).move_both = true;
2839 MarkerDrag::setup_pointer_frame_offset ()
2842 Location *location = _editor->find_location_from_marker (_marker, is_start);
2843 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2847 MarkerDrag::motion (GdkEvent* event, bool)
2849 framecnt_t f_delta = 0;
2851 bool move_both = false;
2852 Location *real_location;
2853 Location *copy_location = 0;
2855 framepos_t const newframe = adjusted_current_frame (event);
2856 framepos_t next = newframe;
2858 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2862 CopiedLocationInfo::iterator x;
2864 /* find the marker we're dragging, and compute the delta */
2866 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2868 copy_location = (*x).location;
2870 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2872 /* this marker is represented by this
2873 * CopiedLocationMarkerInfo
2876 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
2881 if (real_location->is_mark()) {
2882 f_delta = newframe - copy_location->start();
2886 switch (_marker->type()) {
2887 case Marker::SessionStart:
2888 case Marker::RangeStart:
2889 case Marker::LoopStart:
2890 case Marker::PunchIn:
2891 f_delta = newframe - copy_location->start();
2894 case Marker::SessionEnd:
2895 case Marker::RangeEnd:
2896 case Marker::LoopEnd:
2897 case Marker::PunchOut:
2898 f_delta = newframe - copy_location->end();
2901 /* what kind of marker is this ? */
2910 if (x == _copied_locations.end()) {
2911 /* hmm, impossible - we didn't find the dragged marker */
2915 /* now move them all */
2917 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2919 copy_location = x->location;
2921 /* call this to find out if its the start or end */
2923 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
2927 if (real_location->locked()) {
2931 if (copy_location->is_mark()) {
2935 copy_location->set_start (copy_location->start() + f_delta);
2939 framepos_t new_start = copy_location->start() + f_delta;
2940 framepos_t new_end = copy_location->end() + f_delta;
2942 if (is_start) { // start-of-range marker
2944 if (move_both || (*x).move_both) {
2945 copy_location->set_start (new_start);
2946 copy_location->set_end (new_end);
2947 } else if (new_start < copy_location->end()) {
2948 copy_location->set_start (new_start);
2949 } else if (newframe > 0) {
2950 _editor->snap_to (next, 1, true);
2951 copy_location->set_end (next);
2952 copy_location->set_start (newframe);
2955 } else { // end marker
2957 if (move_both || (*x).move_both) {
2958 copy_location->set_end (new_end);
2959 copy_location->set_start (new_start);
2960 } else if (new_end > copy_location->start()) {
2961 copy_location->set_end (new_end);
2962 } else if (newframe > 0) {
2963 _editor->snap_to (next, -1, true);
2964 copy_location->set_start (next);
2965 copy_location->set_end (newframe);
2970 update_item (copy_location);
2972 /* now lookup the actual GUI items used to display this
2973 * location and move them to wherever the copy of the location
2974 * is now. This means that the logic in ARDOUR::Location is
2975 * still enforced, even though we are not (yet) modifying
2976 * the real Location itself.
2979 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2982 lm->set_position (copy_location->start(), copy_location->end());
2987 assert (!_copied_locations.empty());
2989 show_verbose_cursor_time (newframe);
2992 _editor->update_canvas_now ();
2997 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2999 if (!movement_occurred) {
3001 /* just a click, do nothing but finish
3002 off the selection process
3005 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3008 case Selection::Set:
3009 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3010 _editor->selection->set (_marker);
3014 case Selection::Toggle:
3015 /* we toggle on the button release, click only */
3016 _editor->selection->toggle (_marker);
3019 case Selection::Extend:
3020 case Selection::Add:
3027 _editor->_dragging_edit_point = false;
3029 _editor->begin_reversible_command ( _("move marker") );
3030 XMLNode &before = _editor->session()->locations()->get_state();
3032 MarkerSelection::iterator i;
3033 CopiedLocationInfo::iterator x;
3036 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3037 x != _copied_locations.end() && i != _editor->selection->markers.end();
3040 Location * location = _editor->find_location_from_marker (*i, is_start);
3044 if (location->locked()) {
3048 if (location->is_mark()) {
3049 location->set_start (((*x).location)->start());
3051 location->set (((*x).location)->start(), ((*x).location)->end());
3056 XMLNode &after = _editor->session()->locations()->get_state();
3057 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3058 _editor->commit_reversible_command ();
3062 MarkerDrag::aborted (bool)
3068 MarkerDrag::update_item (Location*)
3073 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3075 _cumulative_x_drag (0),
3076 _cumulative_y_drag (0)
3078 if (_zero_gain_fraction < 0.0) {
3079 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3082 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3084 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3090 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3092 Drag::start_grab (event, _editor->cursors()->fader);
3094 // start the grab at the center of the control point so
3095 // the point doesn't 'jump' to the mouse after the first drag
3096 _fixed_grab_x = _point->get_x();
3097 _fixed_grab_y = _point->get_y();
3099 float const fraction = 1 - (_point->get_y() / _point->line().height());
3101 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3103 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3104 event->button.x + 10, event->button.y + 10);
3106 _editor->verbose_cursor()->show ();
3108 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3110 if (!_point->can_slide ()) {
3111 _x_constrained = true;
3116 ControlPointDrag::motion (GdkEvent* event, bool)
3118 double dx = _drags->current_pointer_x() - last_pointer_x();
3119 double dy = _drags->current_pointer_y() - last_pointer_y();
3121 if (event->button.state & Keyboard::SecondaryModifier) {
3126 /* coordinate in pixels relative to the start of the region (for region-based automation)
3127 or track (for track-based automation) */
3128 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3129 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3131 // calculate zero crossing point. back off by .01 to stay on the
3132 // positive side of zero
3133 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3135 // make sure we hit zero when passing through
3136 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3140 if (_x_constrained) {
3143 if (_y_constrained) {
3147 _cumulative_x_drag = cx - _fixed_grab_x;
3148 _cumulative_y_drag = cy - _fixed_grab_y;
3152 cy = min ((double) _point->line().height(), cy);
3154 framepos_t cx_frames = _editor->unit_to_frame (cx);
3156 if (!_x_constrained) {
3157 _editor->snap_to_with_modifier (cx_frames, event);
3160 cx_frames = min (cx_frames, _point->line().maximum_time());
3162 float const fraction = 1.0 - (cy / _point->line().height());
3164 _point->line().drag_motion (_editor->frame_to_unit_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3166 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3170 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3172 if (!movement_occurred) {
3176 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3177 _editor->reset_point_selection ();
3181 motion (event, false);
3184 _point->line().end_drag (_pushing, _final_index);
3185 _editor->session()->commit_reversible_command ();
3189 ControlPointDrag::aborted (bool)
3191 _point->line().reset ();
3195 ControlPointDrag::active (Editing::MouseMode m)
3197 if (m == Editing::MouseGain) {
3198 /* always active in mouse gain */
3202 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3203 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3206 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3209 _cumulative_y_drag (0)
3211 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3215 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3217 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3220 _item = &_line->grab_item ();
3222 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3223 origin, and ditto for y.
3226 double cx = event->button.x;
3227 double cy = event->button.y;
3229 _line->parent_group().w2i (cx, cy);
3231 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
3236 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3237 /* no adjacent points */
3241 Drag::start_grab (event, _editor->cursors()->fader);
3243 /* store grab start in parent frame */
3248 double fraction = 1.0 - (cy / _line->height());
3250 _line->start_drag_line (before, after, fraction);
3252 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3253 event->button.x + 10, event->button.y + 10);
3255 _editor->verbose_cursor()->show ();
3259 LineDrag::motion (GdkEvent* event, bool)
3261 double dy = _drags->current_pointer_y() - last_pointer_y();
3263 if (event->button.state & Keyboard::SecondaryModifier) {
3267 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3269 _cumulative_y_drag = cy - _fixed_grab_y;
3272 cy = min ((double) _line->height(), cy);
3274 double const fraction = 1.0 - (cy / _line->height());
3277 /* we are ignoring x position for this drag, so we can just pass in anything */
3278 _line->drag_motion (0, fraction, true, false, ignored);
3280 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3284 LineDrag::finished (GdkEvent* event, bool)
3286 motion (event, false);
3287 _line->end_drag (false, 0);
3288 _editor->session()->commit_reversible_command ();
3292 LineDrag::aborted (bool)
3297 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3300 _cumulative_x_drag (0)
3302 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3306 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3308 Drag::start_grab (event);
3310 _line = reinterpret_cast<Line*> (_item);
3313 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3315 double cx = event->button.x;
3316 double cy = event->button.y;
3318 _item->property_parent().get_value()->w2i(cx, cy);
3320 /* store grab start in parent frame */
3321 _region_view_grab_x = cx;
3323 _before = *(float*) _item->get_data ("position");
3325 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3327 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3331 FeatureLineDrag::motion (GdkEvent*, bool)
3333 double dx = _drags->current_pointer_x() - last_pointer_x();
3335 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3337 _cumulative_x_drag += dx;
3339 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3348 ArdourCanvas::Points points;
3350 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3352 _line->get_bounds(x1, y2, x2, y2);
3354 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3355 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3357 _line->property_points() = points;
3359 float *pos = new float;
3362 _line->set_data ("position", pos);
3368 FeatureLineDrag::finished (GdkEvent*, bool)
3370 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3371 _arv->update_transient(_before, _before);
3375 FeatureLineDrag::aborted (bool)
3380 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3382 , _vertical_only (false)
3384 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3388 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3390 Drag::start_grab (event);
3391 show_verbose_cursor_time (adjusted_current_frame (event));
3395 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3402 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3404 framepos_t grab = grab_frame ();
3405 if (Config->get_rubberbanding_snaps_to_grid ()) {
3406 _editor->snap_to_with_modifier (grab, event);
3409 /* base start and end on initial click position */
3419 if (_drags->current_pointer_y() < grab_y()) {
3420 y1 = _drags->current_pointer_y();
3423 y2 = _drags->current_pointer_y();
3428 if (start != end || y1 != y2) {
3430 double x1 = _editor->frame_to_pixel (start);
3431 double x2 = _editor->frame_to_pixel (end);
3433 _editor->rubberband_rect->property_x1() = x1;
3434 if (_vertical_only) {
3435 /* fixed 10 pixel width */
3436 _editor->rubberband_rect->property_x2() = x1 + 10;
3438 _editor->rubberband_rect->property_x2() = x2;
3441 _editor->rubberband_rect->property_y1() = y1;
3442 _editor->rubberband_rect->property_y2() = y2;
3444 _editor->rubberband_rect->show();
3445 _editor->rubberband_rect->raise_to_top();
3447 show_verbose_cursor_time (pf);
3449 do_select_things (event, true);
3454 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3459 if (grab_frame() < last_pointer_frame()) {
3461 x2 = last_pointer_frame ();
3464 x1 = last_pointer_frame ();
3470 if (_drags->current_pointer_y() < grab_y()) {
3471 y1 = _drags->current_pointer_y();
3474 y2 = _drags->current_pointer_y();
3478 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3482 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3484 if (movement_occurred) {
3486 motion (event, false);
3487 do_select_things (event, false);
3493 bool do_deselect = true;
3494 MidiTimeAxisView* mtv;
3496 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3498 if (_editor->selection->empty()) {
3499 /* nothing selected */
3500 add_midi_region (mtv);
3501 do_deselect = false;
3505 /* do not deselect if Primary or Tertiary (toggle-select or
3506 * extend-select are pressed.
3509 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3510 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3517 _editor->rubberband_rect->hide();
3521 RubberbandSelectDrag::aborted (bool)
3523 _editor->rubberband_rect->hide ();
3526 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3527 : RegionDrag (e, i, p, v)
3529 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3533 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3535 Drag::start_grab (event, cursor);
3537 show_verbose_cursor_time (adjusted_current_frame (event));
3541 TimeFXDrag::motion (GdkEvent* event, bool)
3543 RegionView* rv = _primary;
3544 StreamView* cv = rv->get_time_axis_view().view ();
3546 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3547 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3548 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3550 framepos_t const pf = adjusted_current_frame (event);
3552 if (pf > rv->region()->position()) {
3553 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3556 show_verbose_cursor_time (pf);
3560 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3562 _primary->get_time_axis_view().hide_timestretch ();
3564 if (!movement_occurred) {
3568 if (last_pointer_frame() < _primary->region()->position()) {
3569 /* backwards drag of the left edge - not usable */
3573 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3575 float percentage = (double) newlen / (double) _primary->region()->length();
3577 #ifndef USE_RUBBERBAND
3578 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3579 if (_primary->region()->data_type() == DataType::AUDIO) {
3580 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3584 if (!_editor->get_selection().regions.empty()) {
3585 /* primary will already be included in the selection, and edit
3586 group shared editing will propagate selection across
3587 equivalent regions, so just use the current region
3591 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3592 error << _("An error occurred while executing time stretch operation") << endmsg;
3598 TimeFXDrag::aborted (bool)
3600 _primary->get_time_axis_view().hide_timestretch ();
3603 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3606 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3610 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3612 Drag::start_grab (event);
3616 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3618 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3622 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3624 if (movement_occurred && _editor->session()) {
3625 /* make sure we stop */
3626 _editor->session()->request_transport_speed (0.0);
3631 ScrubDrag::aborted (bool)
3636 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3641 , _original_pointer_time_axis (-1)
3642 , _last_pointer_time_axis (-1)
3643 , _time_selection_at_start (!_editor->get_selection().time.empty())
3645 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3647 if (_time_selection_at_start) {
3648 start_at_start = _editor->get_selection().time.start();
3649 end_at_start = _editor->get_selection().time.end_frame();
3654 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3656 if (_editor->session() == 0) {
3660 Gdk::Cursor* cursor = 0;
3662 switch (_operation) {
3663 case CreateSelection:
3664 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3669 cursor = _editor->cursors()->selector;
3670 Drag::start_grab (event, cursor);
3673 case SelectionStartTrim:
3674 if (_editor->clicked_axisview) {
3675 _editor->clicked_axisview->order_selection_trims (_item, true);
3677 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3680 case SelectionEndTrim:
3681 if (_editor->clicked_axisview) {
3682 _editor->clicked_axisview->order_selection_trims (_item, false);
3684 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3688 Drag::start_grab (event, cursor);
3691 case SelectionExtend:
3692 Drag::start_grab (event, cursor);
3696 if (_operation == SelectionMove) {
3697 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3699 show_verbose_cursor_time (adjusted_current_frame (event));
3702 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3706 SelectionDrag::setup_pointer_frame_offset ()
3708 switch (_operation) {
3709 case CreateSelection:
3710 _pointer_frame_offset = 0;
3713 case SelectionStartTrim:
3715 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3718 case SelectionEndTrim:
3719 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3722 case SelectionExtend:
3728 SelectionDrag::motion (GdkEvent* event, bool first_move)
3730 framepos_t start = 0;
3732 framecnt_t length = 0;
3733 framecnt_t distance = 0;
3735 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3736 if (pending_time_axis.first == 0) {
3740 framepos_t const pending_position = adjusted_current_frame (event);
3742 /* only alter selection if things have changed */
3744 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3748 switch (_operation) {
3749 case CreateSelection:
3751 framepos_t grab = grab_frame ();
3754 grab = adjusted_current_frame (event, false);
3755 if (grab < pending_position) {
3756 _editor->snap_to (grab, -1);
3758 _editor->snap_to (grab, 1);
3762 if (pending_position < grab) {
3763 start = pending_position;
3766 end = pending_position;
3770 /* first drag: Either add to the selection
3771 or create a new selection
3777 /* adding to the selection */
3778 _editor->set_selected_track_as_side_effect (Selection::Add);
3779 //_editor->selection->add (_editor->clicked_axisview);
3780 _editor->clicked_selection = _editor->selection->add (start, end);
3785 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3786 //_editor->selection->set (_editor->clicked_axisview);
3787 _editor->set_selected_track_as_side_effect (Selection::Set);
3790 _editor->clicked_selection = _editor->selection->set (start, end);
3794 /* select the track that we're in */
3795 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3796 // _editor->set_selected_track_as_side_effect (Selection::Add);
3797 _editor->selection->add (pending_time_axis.first);
3798 _added_time_axes.push_back (pending_time_axis.first);
3801 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3802 tracks that we selected in the first place.
3805 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3806 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3808 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3809 while (i != _added_time_axes.end()) {
3811 list<TimeAxisView*>::iterator tmp = i;
3814 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3815 _editor->selection->remove (*i);
3816 _added_time_axes.remove (*i);
3825 case SelectionStartTrim:
3827 start = _editor->selection->time[_editor->clicked_selection].start;
3828 end = _editor->selection->time[_editor->clicked_selection].end;
3830 if (pending_position > end) {
3833 start = pending_position;
3837 case SelectionEndTrim:
3839 start = _editor->selection->time[_editor->clicked_selection].start;
3840 end = _editor->selection->time[_editor->clicked_selection].end;
3842 if (pending_position < start) {
3845 end = pending_position;
3852 start = _editor->selection->time[_editor->clicked_selection].start;
3853 end = _editor->selection->time[_editor->clicked_selection].end;
3855 length = end - start;
3856 distance = pending_position - start;
3857 start = pending_position;
3858 _editor->snap_to (start);
3860 end = start + length;
3864 case SelectionExtend:
3868 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3869 _editor->start_canvas_autoscroll (1, 0);
3873 switch (_operation) {
3875 if (_time_selection_at_start) {
3876 _editor->selection->move_time (distance);
3880 _editor->selection->replace (_editor->clicked_selection, start, end);
3884 if (_operation == SelectionMove) {
3885 show_verbose_cursor_time(start);
3887 show_verbose_cursor_time(pending_position);
3892 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3894 Session* s = _editor->session();
3896 if (movement_occurred) {
3897 motion (event, false);
3898 /* XXX this is not object-oriented programming at all. ick */
3899 if (_editor->selection->time.consolidate()) {
3900 _editor->selection->TimeChanged ();
3903 /* XXX what if its a music time selection? */
3905 if ( s->get_play_range() && s->transport_rolling() ) {
3906 s->request_play_range (&_editor->selection->time, true);
3908 if (Config->get_always_play_range() && !s->transport_rolling()) {
3909 s->request_locate (_editor->get_selection().time.start());
3915 /* just a click, no pointer movement.
3918 if (_operation == SelectionExtend) {
3919 if (_time_selection_at_start) {
3920 framepos_t pos = adjusted_current_frame (event, false);
3921 framepos_t start = min (pos, start_at_start);
3922 framepos_t end = max (pos, end_at_start);
3923 _editor->selection->set (start, end);
3926 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3927 if (_editor->clicked_selection) {
3928 _editor->selection->remove (_editor->clicked_selection);
3931 if (!_editor->clicked_selection) {
3932 _editor->selection->clear_time();
3937 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3938 _editor->selection->set (_editor->clicked_axisview);
3941 if (s && s->get_play_range () && s->transport_rolling()) {
3942 s->request_stop (false, false);
3947 _editor->stop_canvas_autoscroll ();
3948 _editor->clicked_selection = 0;
3952 SelectionDrag::aborted (bool)
3957 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3962 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3964 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3965 physical_screen_height (_editor->get_window()));
3966 _drag_rect->hide ();
3968 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3969 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3973 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3975 if (_editor->session() == 0) {
3979 Gdk::Cursor* cursor = 0;
3981 if (!_editor->temp_location) {
3982 _editor->temp_location = new Location (*_editor->session());
3985 switch (_operation) {
3986 case CreateRangeMarker:
3987 case CreateTransportMarker:
3988 case CreateCDMarker:
3990 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3995 cursor = _editor->cursors()->selector;
3999 Drag::start_grab (event, cursor);
4001 show_verbose_cursor_time (adjusted_current_frame (event));
4005 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4007 framepos_t start = 0;
4009 ArdourCanvas::SimpleRect *crect;
4011 switch (_operation) {
4012 case CreateRangeMarker:
4013 crect = _editor->range_bar_drag_rect;
4015 case CreateTransportMarker:
4016 crect = _editor->transport_bar_drag_rect;
4018 case CreateCDMarker:
4019 crect = _editor->cd_marker_bar_drag_rect;
4022 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4027 framepos_t const pf = adjusted_current_frame (event);
4029 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4030 framepos_t grab = grab_frame ();
4031 _editor->snap_to (grab);
4033 if (pf < grab_frame()) {
4041 /* first drag: Either add to the selection
4042 or create a new selection.
4047 _editor->temp_location->set (start, end);
4051 update_item (_editor->temp_location);
4053 //_drag_rect->raise_to_top();
4058 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
4059 _editor->start_canvas_autoscroll (1, 0);
4063 _editor->temp_location->set (start, end);
4065 double x1 = _editor->frame_to_pixel (start);
4066 double x2 = _editor->frame_to_pixel (end);
4067 crect->property_x1() = x1;
4068 crect->property_x2() = x2;
4070 update_item (_editor->temp_location);
4073 show_verbose_cursor_time (pf);
4078 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4080 Location * newloc = 0;
4084 if (movement_occurred) {
4085 motion (event, false);
4088 switch (_operation) {
4089 case CreateRangeMarker:
4090 case CreateCDMarker:
4092 _editor->begin_reversible_command (_("new range marker"));
4093 XMLNode &before = _editor->session()->locations()->get_state();
4094 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4095 if (_operation == CreateCDMarker) {
4096 flags = Location::IsRangeMarker | Location::IsCDMarker;
4097 _editor->cd_marker_bar_drag_rect->hide();
4100 flags = Location::IsRangeMarker;
4101 _editor->range_bar_drag_rect->hide();
4103 newloc = new Location (
4104 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4107 _editor->session()->locations()->add (newloc, true);
4108 XMLNode &after = _editor->session()->locations()->get_state();
4109 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4110 _editor->commit_reversible_command ();
4114 case CreateTransportMarker:
4115 // popup menu to pick loop or punch
4116 _editor->new_transport_marker_context_menu (&event->button, _item);
4120 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4122 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
4127 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4129 if (end == max_framepos) {
4130 end = _editor->session()->current_end_frame ();
4133 if (start == max_framepos) {
4134 start = _editor->session()->current_start_frame ();
4137 switch (_editor->mouse_mode) {
4139 /* find the two markers on either side and then make the selection from it */
4140 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4144 /* find the two markers on either side of the click and make the range out of it */
4145 _editor->selection->set (start, end);
4154 _editor->stop_canvas_autoscroll ();
4158 RangeMarkerBarDrag::aborted (bool)
4164 RangeMarkerBarDrag::update_item (Location* location)
4166 double const x1 = _editor->frame_to_pixel (location->start());
4167 double const x2 = _editor->frame_to_pixel (location->end());
4169 _drag_rect->property_x1() = x1;
4170 _drag_rect->property_x2() = x2;
4173 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4177 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4181 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4183 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4184 Drag::start_grab (event, _editor->cursors()->zoom_out);
4187 Drag::start_grab (event, _editor->cursors()->zoom_in);
4191 show_verbose_cursor_time (adjusted_current_frame (event));
4195 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4200 framepos_t const pf = adjusted_current_frame (event);
4202 framepos_t grab = grab_frame ();
4203 _editor->snap_to_with_modifier (grab, event);
4205 /* base start and end on initial click position */
4217 _editor->zoom_rect->show();
4218 _editor->zoom_rect->raise_to_top();
4221 _editor->reposition_zoom_rect(start, end);
4223 show_verbose_cursor_time (pf);
4228 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4230 if (movement_occurred) {
4231 motion (event, false);
4233 if (grab_frame() < last_pointer_frame()) {
4234 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4236 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4239 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4240 _editor->tav_zoom_step (_zoom_out);
4242 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4246 _editor->zoom_rect->hide();
4250 MouseZoomDrag::aborted (bool)
4252 _editor->zoom_rect->hide ();
4255 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4257 , _cumulative_dx (0)
4258 , _cumulative_dy (0)
4260 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4262 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
4263 _region = &_primary->region_view ();
4264 _note_height = _region->midi_stream_view()->note_height ();
4268 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4270 Drag::start_grab (event);
4272 if (!(_was_selected = _primary->selected())) {
4274 /* tertiary-click means extend selection - we'll do that on button release,
4275 so don't add it here, because otherwise we make it hard to figure
4276 out the "extend-to" range.
4279 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4282 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4285 _region->note_selected (_primary, true);
4287 _region->unique_select (_primary);
4293 /** @return Current total drag x change in frames */
4295 NoteDrag::total_dx () const
4298 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
4300 /* primary note time */
4301 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4303 /* new time of the primary note in session frames */
4304 frameoffset_t st = n + dx;
4306 framepos_t const rp = _region->region()->position ();
4308 /* prevent the note being dragged earlier than the region's position */
4311 /* snap and return corresponding delta */
4312 return _region->snap_frame_to_frame (st - rp) + rp - n;
4315 /** @return Current total drag y change in note number */
4317 NoteDrag::total_dy () const
4319 MidiStreamView* msv = _region->midi_stream_view ();
4320 double const y = _region->midi_view()->y_position ();
4321 /* new current note */
4322 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4324 n = max (msv->lowest_note(), n);
4325 n = min (msv->highest_note(), n);
4326 /* and work out delta */
4327 return n - msv->y_to_note (grab_y() - y);
4331 NoteDrag::motion (GdkEvent *, bool)
4333 /* Total change in x and y since the start of the drag */
4334 frameoffset_t const dx = total_dx ();
4335 int8_t const dy = total_dy ();
4337 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4338 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
4339 double const tdy = -dy * _note_height - _cumulative_dy;
4342 _cumulative_dx += tdx;
4343 _cumulative_dy += tdy;
4345 int8_t note_delta = total_dy();
4347 _region->move_selection (tdx, tdy, note_delta);
4349 /* the new note value may be the same as the old one, but we
4350 * don't know what that means because the selection may have
4351 * involved more than one note and we might be doing something
4352 * odd with them. so show the note value anyway, always.
4356 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4358 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4359 (int) floor (new_note));
4361 show_verbose_cursor_text (buf);
4366 NoteDrag::finished (GdkEvent* ev, bool moved)
4369 /* no motion - select note */
4371 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4372 _editor->current_mouse_mode() == Editing::MouseDraw) {
4374 if (_was_selected) {
4375 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4377 _region->note_deselected (_primary);
4380 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4381 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4383 if (!extend && !add && _region->selection_size() > 1) {
4384 _region->unique_select (_primary);
4385 } else if (extend) {
4386 _region->note_selected (_primary, true, true);
4388 /* it was added during button press */
4393 _region->note_dropped (_primary, total_dx(), total_dy());
4398 NoteDrag::aborted (bool)
4403 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4404 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4405 : Drag (editor, atv->base_item ())
4407 , _nothing_to_drag (false)
4409 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4410 y_origin = atv->y_position();
4411 setup (atv->lines ());
4414 /** Make an AutomationRangeDrag for region gain lines */
4415 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4416 : Drag (editor, rv->get_canvas_group ())
4418 , _nothing_to_drag (false)
4420 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4422 list<boost::shared_ptr<AutomationLine> > lines;
4423 lines.push_back (rv->get_gain_line ());
4424 y_origin = rv->get_time_axis_view().y_position();
4428 /** @param lines AutomationLines to drag.
4429 * @param offset Offset from the session start to the points in the AutomationLines.
4432 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4434 /* find the lines that overlap the ranges being dragged */
4435 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4436 while (i != lines.end ()) {
4437 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4440 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4442 /* check this range against all the AudioRanges that we are using */
4443 list<AudioRange>::const_iterator k = _ranges.begin ();
4444 while (k != _ranges.end()) {
4445 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4451 /* add it to our list if it overlaps at all */
4452 if (k != _ranges.end()) {
4457 _lines.push_back (n);
4463 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4467 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4469 return 1.0 - ((global_y - y_origin) / line->height());
4473 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4475 Drag::start_grab (event, cursor);
4477 /* Get line states before we start changing things */
4478 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4479 i->state = &i->line->get_state ();
4480 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4483 if (_ranges.empty()) {
4485 /* No selected time ranges: drag all points */
4486 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4487 uint32_t const N = i->line->npoints ();
4488 for (uint32_t j = 0; j < N; ++j) {
4489 i->points.push_back (i->line->nth (j));
4495 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4497 framecnt_t const half = (i->start + i->end) / 2;
4499 /* find the line that this audio range starts in */
4500 list<Line>::iterator j = _lines.begin();
4501 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4505 if (j != _lines.end()) {
4506 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4508 /* j is the line that this audio range starts in; fade into it;
4509 64 samples length plucked out of thin air.
4512 framepos_t a = i->start + 64;
4517 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4518 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4520 the_list->add (p, the_list->eval (p));
4521 the_list->add (q, the_list->eval (q));
4524 /* same thing for the end */
4527 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4531 if (j != _lines.end()) {
4532 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4534 /* j is the line that this audio range starts in; fade out of it;
4535 64 samples length plucked out of thin air.
4538 framepos_t b = i->end - 64;
4543 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4544 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4546 the_list->add (p, the_list->eval (p));
4547 the_list->add (q, the_list->eval (q));
4551 _nothing_to_drag = true;
4553 /* Find all the points that should be dragged and put them in the relevant
4554 points lists in the Line structs.
4557 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4559 uint32_t const N = i->line->npoints ();
4560 for (uint32_t j = 0; j < N; ++j) {
4562 /* here's a control point on this line */
4563 ControlPoint* p = i->line->nth (j);
4564 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4566 /* see if it's inside a range */
4567 list<AudioRange>::const_iterator k = _ranges.begin ();
4568 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4572 if (k != _ranges.end()) {
4573 /* dragging this point */
4574 _nothing_to_drag = false;
4575 i->points.push_back (p);
4581 if (_nothing_to_drag) {
4585 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4586 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4591 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4593 if (_nothing_to_drag) {
4597 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4598 float const f = y_fraction (l->line, _drags->current_pointer_y());
4599 /* we are ignoring x position for this drag, so we can just pass in anything */
4601 l->line->drag_motion (0, f, true, false, ignored);
4602 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4607 AutomationRangeDrag::finished (GdkEvent* event, bool)
4609 if (_nothing_to_drag) {
4613 motion (event, false);
4614 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4615 i->line->end_drag (false, 0);
4618 _editor->session()->commit_reversible_command ();
4622 AutomationRangeDrag::aborted (bool)
4624 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4629 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4632 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4633 layer = v->region()->layer ();
4634 initial_y = v->get_canvas_group()->property_y ();
4635 initial_playlist = v->region()->playlist ();
4636 initial_position = v->region()->position ();
4637 initial_end = v->region()->position () + v->region()->length ();
4640 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4644 , _cumulative_dx (0)
4646 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4647 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4652 PatchChangeDrag::motion (GdkEvent* ev, bool)
4654 framepos_t f = adjusted_current_frame (ev);
4655 boost::shared_ptr<Region> r = _region_view->region ();
4656 f = max (f, r->position ());
4657 f = min (f, r->last_frame ());
4659 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4660 double const dxu = _editor->frame_to_unit (dxf); // permitted fx in units
4661 _patch_change->move (dxu - _cumulative_dx, 0);
4662 _cumulative_dx = dxu;
4666 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4668 if (!movement_occurred) {
4672 boost::shared_ptr<Region> r (_region_view->region ());
4673 framepos_t f = adjusted_current_frame (ev);
4674 f = max (f, r->position ());
4675 f = min (f, r->last_frame ());
4677 _region_view->move_patch_change (
4679 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4684 PatchChangeDrag::aborted (bool)
4686 _patch_change->move (-_cumulative_dx, 0);
4690 PatchChangeDrag::setup_pointer_frame_offset ()
4692 boost::shared_ptr<Region> region = _region_view->region ();
4693 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4696 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4697 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4704 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4706 framepos_t const p = _region_view->region()->position ();
4707 double const y = _region_view->midi_view()->y_position ();
4709 x1 = max ((framepos_t) 0, x1 - p);
4710 x2 = max ((framepos_t) 0, x2 - p);
4711 y1 = max (0.0, y1 - y);
4712 y2 = max (0.0, y2 - y);
4714 _region_view->update_drag_selection (
4715 _editor->frame_to_pixel (x1),
4716 _editor->frame_to_pixel (x2),
4719 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4724 MidiRubberbandSelectDrag::deselect_things ()
4729 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4730 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4733 _vertical_only = true;
4737 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4739 double const y = _region_view->midi_view()->y_position ();
4741 y1 = max (0.0, y1 - y);
4742 y2 = max (0.0, y2 - y);
4744 _region_view->update_vertical_drag_selection (
4747 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4752 MidiVerticalSelectDrag::deselect_things ()
4757 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4758 : RubberbandSelectDrag (e, i)
4764 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4766 if (drag_in_progress) {
4767 /* We just want to select things at the end of the drag, not during it */
4771 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4773 _editor->begin_reversible_command (_("rubberband selection"));
4774 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4775 _editor->commit_reversible_command ();
4779 EditorRubberbandSelectDrag::deselect_things ()
4781 if (!getenv("ARDOUR_SAE")) {
4782 _editor->selection->clear_tracks();
4784 _editor->selection->clear_regions();
4785 _editor->selection->clear_points ();
4786 _editor->selection->clear_lines ();
4789 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4797 NoteCreateDrag::~NoteCreateDrag ()
4803 NoteCreateDrag::grid_frames (framepos_t t) const
4806 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4811 return _region_view->region_beats_to_region_frames (grid_beats);
4815 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4817 Drag::start_grab (event, cursor);
4819 _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4821 framepos_t pf = _drags->current_pointer_frame ();
4822 framecnt_t const g = grid_frames (pf);
4824 /* Hack so that we always snap to the note that we are over, instead of snapping
4825 to the next one if we're more than halfway through the one we're over.
4827 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4831 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4833 MidiStreamView* sv = _region_view->midi_stream_view ();
4834 double const x = _editor->frame_to_pixel (_note[0]);
4835 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4837 _drag_rect->property_x1() = x;
4838 _drag_rect->property_y1() = y;
4839 _drag_rect->property_x2() = x;
4840 _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4842 _drag_rect->property_outline_what() = 0xff;
4843 _drag_rect->property_outline_color_rgba() = 0xffffff99;
4844 _drag_rect->property_fill_color_rgba() = 0xffffff66;
4848 NoteCreateDrag::motion (GdkEvent* event, bool)
4850 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4851 double const x = _editor->frame_to_pixel (_note[1]);
4852 if (_note[1] > _note[0]) {
4853 _drag_rect->property_x2() = x;
4855 _drag_rect->property_x1() = x;
4860 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4862 if (!had_movement) {
4866 framepos_t const start = min (_note[0], _note[1]);
4867 framecnt_t length = (framecnt_t) fabs (_note[0] - _note[1]);
4869 framecnt_t const g = grid_frames (start);
4870 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4872 if (_editor->snap_mode() == SnapNormal && length < g) {
4873 length = g - one_tick;
4876 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4878 _region_view->create_note_at (start, _drag_rect->property_y1(), length_beats, false);
4882 NoteCreateDrag::y_to_region (double y) const
4885 _region_view->get_canvas_group()->w2i (x, y);
4890 NoteCreateDrag::aborted (bool)
4895 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4900 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
4904 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4906 Drag::start_grab (event, cursor);
4910 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4916 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4919 distance = _drags->current_pointer_x() - grab_x();
4920 len = ar->fade_in()->back()->when;
4922 distance = grab_x() - _drags->current_pointer_x();
4923 len = ar->fade_out()->back()->when;
4926 /* how long should it be ? */
4928 new_length = len + _editor->unit_to_frame (distance);
4930 /* now check with the region that this is legal */
4932 new_length = ar->verify_xfade_bounds (new_length, start);
4935 arv->redraw_start_xfade_to (ar, new_length);
4937 arv->redraw_end_xfade_to (ar, new_length);
4942 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
4948 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4951 distance = _drags->current_pointer_x() - grab_x();
4952 len = ar->fade_in()->back()->when;
4954 distance = grab_x() - _drags->current_pointer_x();
4955 len = ar->fade_out()->back()->when;
4958 new_length = ar->verify_xfade_bounds (len + _editor->unit_to_frame (distance), start);
4960 _editor->begin_reversible_command ("xfade trim");
4961 ar->playlist()->clear_owned_changes ();
4964 ar->set_fade_in_length (new_length);
4966 ar->set_fade_out_length (new_length);
4969 /* Adjusting the xfade may affect other regions in the playlist, so we need
4970 to get undo Commands from the whole playlist rather than just the
4974 vector<Command*> cmds;
4975 ar->playlist()->rdiff (cmds);
4976 _editor->session()->add_commands (cmds);
4977 _editor->commit_reversible_command ();
4982 CrossfadeEdgeDrag::aborted (bool)
4985 arv->redraw_start_xfade ();
4987 arv->redraw_end_xfade ();