2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/dB.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/operations.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/session.h"
44 #include "audio_region_view.h"
45 #include "midi_region_view.h"
46 #include "ardour_ui.h"
47 #include "gui_thread.h"
48 #include "control_point.h"
50 #include "region_gain_line.h"
51 #include "editor_drag.h"
52 #include "audio_time_axis.h"
53 #include "midi_time_axis.h"
54 #include "canvas-note.h"
55 #include "selection.h"
56 #include "midi_selection.h"
57 #include "automation_time_axis.h"
59 #include "editor_cursors.h"
60 #include "mouse_cursors.h"
61 #include "verbose_cursor.h"
64 using namespace ARDOUR;
67 using namespace Gtkmm2ext;
68 using namespace Editing;
69 using namespace ArdourCanvas;
71 using Gtkmm2ext::Keyboard;
73 double ControlPointDrag::_zero_gain_fraction = -1.0;
75 DragManager::DragManager (Editor* e)
78 , _current_pointer_frame (0)
82 DragManager::~DragManager ()
87 /** Call abort for each active drag */
93 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
98 if (!_drags.empty ()) {
99 _editor->set_follow_playhead (_old_follow_playhead, false);
108 DragManager::add (Drag* d)
110 d->set_manager (this);
111 _drags.push_back (d);
115 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
117 d->set_manager (this);
118 _drags.push_back (d);
123 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
125 /* Prevent follow playhead during the drag to be nice to the user */
126 _old_follow_playhead = _editor->follow_playhead ();
127 _editor->set_follow_playhead (false);
129 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
131 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
132 (*i)->start_grab (e, c);
136 /** Call end_grab for each active drag.
137 * @return true if any drag reported movement having occurred.
140 DragManager::end_grab (GdkEvent* e)
145 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
146 bool const t = (*i)->end_grab (e);
157 _editor->set_follow_playhead (_old_follow_playhead, false);
163 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
167 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
169 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
170 bool const t = (*i)->motion_handler (e, from_autoscroll);
181 DragManager::have_item (ArdourCanvas::Item* i) const
183 list<Drag*>::const_iterator j = _drags.begin ();
184 while (j != _drags.end() && (*j)->item () != i) {
188 return j != _drags.end ();
191 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
194 , _pointer_frame_offset (0)
195 , _move_threshold_passed (false)
196 , _raw_grab_frame (0)
198 , _last_pointer_frame (0)
204 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
210 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, time);
212 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
217 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
219 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
221 if (Keyboard::is_button2_event (&event->button)) {
222 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
223 _y_constrained = true;
224 _x_constrained = false;
226 _y_constrained = false;
227 _x_constrained = true;
230 _x_constrained = false;
231 _y_constrained = false;
234 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
235 setup_pointer_frame_offset ();
236 _grab_frame = adjusted_frame (_raw_grab_frame, event);
237 _last_pointer_frame = _grab_frame;
238 _last_pointer_x = _grab_x;
239 _last_pointer_y = _grab_y;
242 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
245 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
250 if (_editor->session() && _editor->session()->transport_rolling()) {
253 _was_rolling = false;
256 switch (_editor->snap_type()) {
257 case SnapToRegionStart:
258 case SnapToRegionEnd:
259 case SnapToRegionSync:
260 case SnapToRegionBoundary:
261 _editor->build_region_boundary_cache ();
268 /** Call to end a drag `successfully'. Ungrabs item and calls
269 * subclass' finished() method.
271 * @param event GDK event, or 0.
272 * @return true if some movement occurred, otherwise false.
275 Drag::end_grab (GdkEvent* event)
277 _editor->stop_canvas_autoscroll ();
279 _item->ungrab (event ? event->button.time : 0);
281 finished (event, _move_threshold_passed);
283 _editor->verbose_cursor()->hide ();
285 return _move_threshold_passed;
289 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
293 if (f > _pointer_frame_offset) {
294 pos = f - _pointer_frame_offset;
298 _editor->snap_to_with_modifier (pos, event);
305 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
307 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
311 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
313 /* check to see if we have moved in any way that matters since the last motion event */
314 if (_move_threshold_passed &&
315 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
316 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
320 pair<framecnt_t, int> const threshold = move_threshold ();
322 bool const old_move_threshold_passed = _move_threshold_passed;
324 if (!from_autoscroll && !_move_threshold_passed) {
326 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
327 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
329 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
332 if (active (_editor->mouse_mode) && _move_threshold_passed) {
334 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
335 if (!from_autoscroll) {
336 bool const moving_left = _drags->current_pointer_x() < _last_pointer_x;
337 bool const moving_up = _drags->current_pointer_y() < _last_pointer_y;
338 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), moving_left, moving_up);
341 motion (event, _move_threshold_passed != old_move_threshold_passed);
343 _last_pointer_x = _drags->current_pointer_x ();
344 _last_pointer_y = _drags->current_pointer_y ();
345 _last_pointer_frame = adjusted_current_frame (event);
353 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
361 aborted (_move_threshold_passed);
363 _editor->stop_canvas_autoscroll ();
364 _editor->verbose_cursor()->hide ();
368 Drag::show_verbose_cursor_time (framepos_t frame)
370 _editor->verbose_cursor()->set_time (
372 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
373 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
376 _editor->verbose_cursor()->show ();
380 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
382 _editor->verbose_cursor()->show (xoffset);
384 _editor->verbose_cursor()->set_duration (
386 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
387 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
392 Drag::show_verbose_cursor_text (string const & text)
394 _editor->verbose_cursor()->show ();
396 _editor->verbose_cursor()->set (
398 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
399 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
403 boost::shared_ptr<Region>
404 Drag::add_midi_region (MidiTimeAxisView* view)
406 if (_editor->session()) {
407 const TempoMap& map (_editor->session()->tempo_map());
408 framecnt_t pos = grab_frame();
409 const Meter& m = map.meter_at (pos);
410 /* not that the frame rate used here can be affected by pull up/down which
413 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
414 return view->add_region (grab_frame(), len, true);
417 return boost::shared_ptr<Region>();
420 struct EditorOrderTimeAxisViewSorter {
421 bool operator() (TimeAxisView* a, TimeAxisView* b) {
422 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
423 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
425 return ra->route()->order_key (EditorSort) < rb->route()->order_key (EditorSort);
429 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
433 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
435 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
436 as some of the regions we are dragging may be on such tracks.
439 TrackViewList track_views = _editor->track_views;
440 track_views.sort (EditorOrderTimeAxisViewSorter ());
442 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
443 _time_axis_views.push_back (*i);
445 TimeAxisView::Children children_list = (*i)->get_child_list ();
446 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
447 _time_axis_views.push_back (j->get());
451 /* the list of views can be empty at this point if this is a region list-insert drag
454 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
455 _views.push_back (DraggingView (*i, this));
458 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
462 RegionDrag::region_going_away (RegionView* v)
464 list<DraggingView>::iterator i = _views.begin ();
465 while (i != _views.end() && i->view != v) {
469 if (i != _views.end()) {
474 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
475 * or -1 if it is not found.
478 RegionDrag::find_time_axis_view (TimeAxisView* t) const
481 int const N = _time_axis_views.size ();
482 while (i < N && _time_axis_views[i] != t) {
493 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
494 : RegionDrag (e, i, p, v),
503 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
505 Drag::start_grab (event, cursor);
507 show_verbose_cursor_time (_last_frame_position);
509 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
510 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
511 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
515 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
517 /* compute the amount of pointer motion in frames, and where
518 the region would be if we moved it by that much.
520 *pending_region_position = adjusted_current_frame (event);
522 framepos_t sync_frame;
523 framecnt_t sync_offset;
526 sync_offset = _primary->region()->sync_offset (sync_dir);
528 /* we don't handle a sync point that lies before zero.
530 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
532 sync_frame = *pending_region_position + (sync_dir*sync_offset);
534 _editor->snap_to_with_modifier (sync_frame, event);
536 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
539 *pending_region_position = _last_frame_position;
542 if (*pending_region_position > max_framepos - _primary->region()->length()) {
543 *pending_region_position = _last_frame_position;
548 /* in locked edit mode, reverse the usual meaning of _x_constrained */
549 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
551 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
553 /* x movement since last time (in pixels) */
554 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
556 /* total x movement */
557 framecnt_t total_dx = *pending_region_position;
558 if (regions_came_from_canvas()) {
559 total_dx = total_dx - grab_frame ();
562 /* check that no regions have gone off the start of the session */
563 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
564 if ((i->view->region()->position() + total_dx) < 0) {
566 *pending_region_position = _last_frame_position;
571 _last_frame_position = *pending_region_position;
578 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
580 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
581 int const n = i->time_axis_view + delta_track;
582 if (n < 0 || n >= int (_time_axis_views.size())) {
583 /* off the top or bottom track */
587 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
588 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
589 /* not a track, or the wrong type */
593 double const l = i->layer + delta_layer;
595 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
596 mode to allow the user to place a region below another on layer 0.
598 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
599 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
600 If it has, the layers will be munged later anyway, so it's ok.
606 /* all regions being dragged are ok with this change */
611 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
613 assert (!_views.empty ());
615 /* Find the TimeAxisView that the pointer is now over */
616 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
618 if (first_move && tv.first->view()->layer_display() == Stacked) {
619 tv.first->view()->set_layer_display (Expanded);
622 /* Bail early if we're not over a track */
623 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
624 if (!rtv || !rtv->is_track()) {
625 _editor->verbose_cursor()->hide ();
629 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
631 /* Here's the current pointer position in terms of time axis view and layer */
632 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
633 double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
635 /* Work out the change in x */
636 framepos_t pending_region_position;
637 double const x_delta = compute_x_delta (event, &pending_region_position);
639 /* Work out the change in y */
640 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
641 double delta_layer = current_pointer_layer - _last_pointer_layer;
643 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
644 /* this y movement is not allowed, so do no y movement this time */
645 delta_time_axis_view = 0;
649 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
650 /* haven't reached next snap point, and we're not switching
651 trackviews nor layers. nothing to do.
656 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
658 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
660 RegionView* rv = i->view;
662 if (rv->region()->locked()
663 #ifdef WITH_VIDEOTIMELINE
664 || rv->region()->video_locked()
674 /* Absolutely no idea why this is necessary, but it is; without
675 it, the region view disappears after the reparent.
677 _editor->update_canvas_now ();
679 /* Reparent to a non scrolling group so that we can keep the
680 region selection above all time axis views.
681 Reparenting means that we will have to move the region view
682 later, as the two parent groups have different coordinates.
685 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
687 rv->fake_set_opaque (true);
690 /* If we have moved tracks, we'll fudge the layer delta so that the
691 region gets moved back onto layer 0 on its new track; this avoids
692 confusion when dragging regions from non-zero layers onto different
695 double this_delta_layer = delta_layer;
696 if (delta_time_axis_view != 0) {
697 this_delta_layer = - i->layer;
700 /* The TimeAxisView that this region is now on */
701 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
703 /* Ensure it is moved from stacked -> expanded if appropriate */
704 if (tv->view()->layer_display() == Stacked) {
705 tv->view()->set_layer_display (Expanded);
708 /* We're only allowed to go -ve in layer on Expanded views */
709 if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
710 this_delta_layer = - i->layer;
714 rv->set_height (tv->view()->child_height ());
716 /* Update show/hidden status as the region view may have come from a hidden track,
717 or have moved to one.
720 rv->get_canvas_group()->hide ();
722 rv->get_canvas_group()->show ();
725 /* Update the DraggingView */
726 i->time_axis_view += delta_time_axis_view;
727 i->layer += this_delta_layer;
730 _editor->mouse_brush_insert_region (rv, pending_region_position);
735 /* Get the y coordinate of the top of the track that this region is now on */
736 tv->canvas_display()->i2w (x, y);
737 y += _editor->get_trackview_group_vertical_offset();
739 /* And adjust for the layer that it should be on */
740 StreamView* cv = tv->view ();
741 switch (cv->layer_display ()) {
745 y += (cv->layers() - i->layer - 1) * cv->child_height ();
748 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
752 /* Now move the region view */
753 rv->move (x_delta, y - rv->get_canvas_group()->property_y());
756 } /* foreach region */
758 _total_x_delta += x_delta;
761 _editor->cursor_group->raise_to_top();
764 if (x_delta != 0 && !_brushing) {
765 show_verbose_cursor_time (_last_frame_position);
768 _last_pointer_time_axis_view += delta_time_axis_view;
769 _last_pointer_layer += delta_layer;
773 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
775 if (_copy && first_move) {
777 /* duplicate the regionview(s) and region(s) */
779 list<DraggingView> new_regionviews;
781 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
783 RegionView* rv = i->view;
784 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
785 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
787 const boost::shared_ptr<const Region> original = rv->region();
788 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
789 region_copy->set_position (original->position());
793 boost::shared_ptr<AudioRegion> audioregion_copy
794 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
796 nrv = new AudioRegionView (*arv, audioregion_copy);
798 boost::shared_ptr<MidiRegion> midiregion_copy
799 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
800 nrv = new MidiRegionView (*mrv, midiregion_copy);
805 nrv->get_canvas_group()->show ();
806 new_regionviews.push_back (DraggingView (nrv, this));
808 /* swap _primary to the copy */
810 if (rv == _primary) {
814 /* ..and deselect the one we copied */
816 rv->set_selected (false);
819 if (!new_regionviews.empty()) {
821 /* reflect the fact that we are dragging the copies */
823 _views = new_regionviews;
825 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
828 sync the canvas to what we think is its current state
829 without it, the canvas seems to
830 "forget" to update properly after the upcoming reparent()
831 ..only if the mouse is in rapid motion at the time of the grab.
832 something to do with regionview creation taking so long?
834 _editor->update_canvas_now();
838 RegionMotionDrag::motion (event, first_move);
842 RegionMotionDrag::finished (GdkEvent *, bool)
844 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
849 if ((*i)->view()->layer_display() == Expanded) {
850 (*i)->view()->set_layer_display (Stacked);
856 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
858 RegionMotionDrag::finished (ev, movement_occurred);
860 if (!movement_occurred) {
865 /* reverse this here so that we have the correct logic to finalize
869 if (Config->get_edit_mode() == Lock) {
870 _x_constrained = !_x_constrained;
873 assert (!_views.empty ());
875 /* We might have hidden region views so that they weren't visible during the drag
876 (when they have been reparented). Now everything can be shown again, as region
877 views are back in their track parent groups.
879 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
880 i->view->get_canvas_group()->show ();
883 bool const changed_position = (_last_frame_position != _primary->region()->position());
884 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
885 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
887 _editor->update_canvas_now ();
907 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
911 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
913 RegionSelection new_views;
914 PlaylistSet modified_playlists;
915 list<RegionView*> views_to_delete;
918 /* all changes were made during motion event handlers */
920 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
924 _editor->commit_reversible_command ();
928 if (_x_constrained) {
929 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
931 _editor->begin_reversible_command (Operations::region_copy);
934 /* insert the regions into their new playlists */
935 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
937 if (i->view->region()->locked()
938 #ifdef WITH_VIDEOTIMELINE
939 || i->view->region()->video_locked()
947 if (changed_position && !_x_constrained) {
948 where = i->view->region()->position() - drag_delta;
950 where = i->view->region()->position();
953 RegionView* new_view = insert_region_into_playlist (
954 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
961 new_views.push_back (new_view);
963 /* we don't need the copied RegionView any more */
964 views_to_delete.push_back (i->view);
967 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
968 because when views are deleted they are automagically removed from _views, which messes
971 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
975 /* If we've created new regions either by copying or moving
976 to a new track, we want to replace the old selection with the new ones
979 if (new_views.size() > 0) {
980 _editor->selection->set (new_views);
983 /* write commands for the accumulated diffs for all our modified playlists */
984 add_stateful_diff_commands_for_playlists (modified_playlists);
986 _editor->commit_reversible_command ();
990 RegionMoveDrag::finished_no_copy (
991 bool const changed_position,
992 bool const changed_tracks,
993 framecnt_t const drag_delta
996 RegionSelection new_views;
997 PlaylistSet modified_playlists;
998 PlaylistSet frozen_playlists;
999 set<RouteTimeAxisView*> views_to_update;
1002 /* all changes were made during motion event handlers */
1003 _editor->commit_reversible_command ();
1007 if (_x_constrained) {
1008 _editor->begin_reversible_command (_("fixed time region drag"));
1010 _editor->begin_reversible_command (Operations::region_drag);
1013 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1015 RegionView* rv = i->view;
1017 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1018 double const dest_layer = i->layer;
1020 if (rv->region()->locked()
1021 #ifdef WITH_VIDEOTIMELINE
1022 || rv->region()->video_locked()
1029 views_to_update.insert (dest_rtv);
1033 if (changed_position && !_x_constrained) {
1034 where = rv->region()->position() - drag_delta;
1036 where = rv->region()->position();
1039 if (changed_tracks) {
1041 /* insert into new playlist */
1043 RegionView* new_view = insert_region_into_playlist (
1044 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1047 if (new_view == 0) {
1052 new_views.push_back (new_view);
1054 /* remove from old playlist */
1056 /* the region that used to be in the old playlist is not
1057 moved to the new one - we use a copy of it. as a result,
1058 any existing editor for the region should no longer be
1061 rv->hide_region_editor();
1062 rv->fake_set_opaque (false);
1064 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1068 rv->region()->clear_changes ();
1071 motion on the same track. plonk the previously reparented region
1072 back to its original canvas group (its streamview).
1073 No need to do anything for copies as they are fake regions which will be deleted.
1076 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1077 rv->get_canvas_group()->property_y() = i->initial_y;
1080 /* just change the model */
1082 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1084 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1085 playlist->set_layer (rv->region(), dest_layer);
1088 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1090 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1093 playlist->freeze ();
1096 /* this movement may result in a crossfade being modified, so we need to get undo
1097 data from the playlist as well as the region.
1100 r = modified_playlists.insert (playlist);
1102 playlist->clear_changes ();
1105 rv->region()->set_position (where);
1107 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1110 if (changed_tracks) {
1112 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1113 was selected in all of them, then removing it from a playlist will have removed all
1114 trace of it from _views (i.e. there were N regions selected, we removed 1,
1115 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1116 corresponding regionview, and _views is now empty).
1118 This could have invalidated any and all iterators into _views.
1120 The heuristic we use here is: if the region selection is empty, break out of the loop
1121 here. if the region selection is not empty, then restart the loop because we know that
1122 we must have removed at least the region(view) we've just been working on as well as any
1123 that we processed on previous iterations.
1125 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1126 we can just iterate.
1130 if (_views.empty()) {
1141 /* If we've created new regions either by copying or moving
1142 to a new track, we want to replace the old selection with the new ones
1145 if (new_views.size() > 0) {
1146 _editor->selection->set (new_views);
1149 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1153 /* write commands for the accumulated diffs for all our modified playlists */
1154 add_stateful_diff_commands_for_playlists (modified_playlists);
1156 _editor->commit_reversible_command ();
1158 /* We have futzed with the layering of canvas items on our streamviews.
1159 If any region changed layer, this will have resulted in the stream
1160 views being asked to set up their region views, and all will be well.
1161 If not, we might now have badly-ordered region views. Ask the StreamViews
1162 involved to sort themselves out, just in case.
1165 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1166 (*i)->view()->playlist_layered ((*i)->track ());
1170 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1171 * @param region Region to remove.
1172 * @param playlist playlist To remove from.
1173 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1174 * that clear_changes () is only called once per playlist.
1177 RegionMoveDrag::remove_region_from_playlist (
1178 boost::shared_ptr<Region> region,
1179 boost::shared_ptr<Playlist> playlist,
1180 PlaylistSet& modified_playlists
1183 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1186 playlist->clear_changes ();
1189 playlist->remove_region (region);
1193 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1194 * clearing the playlist's diff history first if necessary.
1195 * @param region Region to insert.
1196 * @param dest_rtv Destination RouteTimeAxisView.
1197 * @param dest_layer Destination layer.
1198 * @param where Destination position.
1199 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1200 * that clear_changes () is only called once per playlist.
1201 * @return New RegionView, or 0 if no insert was performed.
1204 RegionMoveDrag::insert_region_into_playlist (
1205 boost::shared_ptr<Region> region,
1206 RouteTimeAxisView* dest_rtv,
1209 PlaylistSet& modified_playlists
1212 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1213 if (!dest_playlist) {
1217 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1218 _new_region_view = 0;
1219 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1221 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1222 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1224 dest_playlist->clear_changes ();
1227 dest_playlist->add_region (region, where);
1229 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1230 dest_playlist->set_layer (region, dest_layer);
1235 assert (_new_region_view);
1237 return _new_region_view;
1241 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1243 _new_region_view = rv;
1247 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1249 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1250 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1252 _editor->session()->add_command (c);
1261 RegionMoveDrag::aborted (bool movement_occurred)
1265 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1272 RegionMotionDrag::aborted (movement_occurred);
1277 RegionMotionDrag::aborted (bool)
1279 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1280 if ((*i)->view()->layer_display() == Expanded) {
1281 (*i)->view()->set_layer_display (Stacked);
1285 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1286 RegionView* rv = i->view;
1287 TimeAxisView* tv = &(rv->get_time_axis_view ());
1288 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1290 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1291 rv->get_canvas_group()->property_y() = 0;
1293 rv->fake_set_opaque (false);
1294 rv->move (-_total_x_delta, 0);
1295 rv->set_height (rtv->view()->child_height ());
1298 _editor->update_canvas_now ();
1301 /** @param b true to brush, otherwise false.
1302 * @param c true to make copies of the regions being moved, otherwise false.
1304 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1305 : RegionMotionDrag (e, i, p, v, b),
1308 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1311 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1312 if (rtv && rtv->is_track()) {
1313 speed = rtv->track()->speed ();
1316 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1320 RegionMoveDrag::setup_pointer_frame_offset ()
1322 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1325 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1326 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1328 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1330 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1331 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1333 _primary = v->view()->create_region_view (r, false, false);
1335 _primary->get_canvas_group()->show ();
1336 _primary->set_position (pos, 0);
1337 _views.push_back (DraggingView (_primary, this));
1339 _last_frame_position = pos;
1341 _item = _primary->get_canvas_group ();
1345 RegionInsertDrag::finished (GdkEvent *, bool)
1347 _editor->update_canvas_now ();
1349 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1351 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1352 _primary->get_canvas_group()->property_y() = 0;
1354 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1356 _editor->begin_reversible_command (Operations::insert_region);
1357 playlist->clear_changes ();
1358 playlist->add_region (_primary->region (), _last_frame_position);
1359 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1360 _editor->commit_reversible_command ();
1368 RegionInsertDrag::aborted (bool)
1375 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1376 : RegionMoveDrag (e, i, p, v, false, false)
1378 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1381 struct RegionSelectionByPosition {
1382 bool operator() (RegionView*a, RegionView* b) {
1383 return a->region()->position () < b->region()->position();
1388 RegionSpliceDrag::motion (GdkEvent* event, bool)
1390 /* Which trackview is this ? */
1392 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1393 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1395 /* The region motion is only processed if the pointer is over
1399 if (!tv || !tv->is_track()) {
1400 /* To make sure we hide the verbose canvas cursor when the mouse is
1401 not held over and audiotrack.
1403 _editor->verbose_cursor()->hide ();
1409 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1415 RegionSelection copy (_editor->selection->regions);
1417 RegionSelectionByPosition cmp;
1420 framepos_t const pf = adjusted_current_frame (event);
1422 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1424 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1430 boost::shared_ptr<Playlist> playlist;
1432 if ((playlist = atv->playlist()) == 0) {
1436 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1441 if (pf < (*i)->region()->last_frame() + 1) {
1445 if (pf > (*i)->region()->first_frame()) {
1451 playlist->shuffle ((*i)->region(), dir);
1456 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1458 RegionMoveDrag::finished (event, movement_occurred);
1462 RegionSpliceDrag::aborted (bool)
1467 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1469 _view (dynamic_cast<MidiTimeAxisView*> (v))
1471 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1477 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1480 _region = add_midi_region (_view);
1481 _view->playlist()->freeze ();
1484 framepos_t const f = adjusted_current_frame (event);
1485 if (f < grab_frame()) {
1486 _region->set_position (f);
1489 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1490 so that if this region is duplicated, its duplicate starts on
1491 a snap point rather than 1 frame after a snap point. Otherwise things get
1492 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1493 place snapped notes at the start of the region.
1496 framecnt_t const len = (framecnt_t) fabs (f - grab_frame () - 1);
1497 _region->set_length (len < 1 ? 1 : len);
1503 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1505 if (!movement_occurred) {
1506 add_midi_region (_view);
1508 _view->playlist()->thaw ();
1513 RegionCreateDrag::aborted (bool)
1516 _view->playlist()->thaw ();
1522 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1526 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1530 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1532 Gdk::Cursor* cursor;
1533 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1534 float x_fraction = cnote->mouse_x_fraction ();
1536 if (x_fraction > 0.0 && x_fraction < 0.25) {
1537 cursor = _editor->cursors()->left_side_trim;
1539 cursor = _editor->cursors()->right_side_trim;
1542 Drag::start_grab (event, cursor);
1544 region = &cnote->region_view();
1546 double const region_start = region->get_position_pixels();
1547 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1549 if (grab_x() <= middle_point) {
1550 cursor = _editor->cursors()->left_side_trim;
1553 cursor = _editor->cursors()->right_side_trim;
1557 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1559 if (event->motion.state & Keyboard::PrimaryModifier) {
1565 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1567 if (ms.size() > 1) {
1568 /* has to be relative, may make no sense otherwise */
1572 /* select this note; if it is already selected, preserve the existing selection,
1573 otherwise make this note the only one selected.
1575 region->note_selected (cnote, cnote->selected ());
1577 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1578 MidiRegionSelection::iterator next;
1581 (*r)->begin_resizing (at_front);
1587 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1589 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1590 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1591 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1596 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1598 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1599 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1600 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1605 NoteResizeDrag::aborted (bool)
1607 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1608 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1609 (*r)->abort_resizing ();
1613 #ifdef WITH_VIDEOTIMELINE
1615 AVDraggingView::AVDraggingView (RegionView* v)
1618 initial_position = v->region()->position ();
1621 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1624 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1626 /* create a list of regions to move along */
1627 #if 1 /* all reagions -- with video_locked() */
1629 TrackViewList empty;
1631 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1632 std::list<RegionView*> views = rs.by_layer();
1633 #else /* selected regions -- with video_locked() */
1634 std::list<RegionView*> views = _editor->selection->regions.by_layer();
1636 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1637 RegionView* rv = (*i);
1638 if (!rv->region()->video_locked()) {
1641 _views.push_back (AVDraggingView (rv));
1646 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1648 Drag::start_grab (event);
1649 if (_editor->session() == 0) {
1653 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1654 _max_backwards_drag = (
1655 ARDOUR_UI::instance()->video_timeline->get_duration()
1656 + ARDOUR_UI::instance()->video_timeline->get_offset()
1657 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1660 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1661 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1662 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1665 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1668 Timecode::Time timecode;
1669 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1670 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);
1671 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1672 _editor->verbose_cursor()->show ();
1676 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1678 if (_editor->session() == 0) {
1681 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1685 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1686 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(dt);
1688 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1689 dt = - _max_backwards_drag;
1692 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1693 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1695 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1696 RegionView* rv = i->view;
1697 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1700 _editor->update_canvas_now ();
1701 rv->fake_set_opaque (true);
1702 rv->region()->clear_changes ();
1703 rv->region()->suspend_property_changes();
1705 rv->region()->set_position(i->initial_position + dt);
1706 rv->region_changed(ARDOUR::Properties::position);
1709 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1710 Timecode::Time timecode;
1711 Timecode::Time timediff;
1713 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1714 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1715 snprintf (buf, sizeof (buf),
1716 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1717 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1718 , _("Video Start:"),
1719 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1721 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1723 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1724 _editor->verbose_cursor()->show ();
1728 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1730 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1734 if (!movement_occurred || ! _editor->session()) {
1738 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1740 _editor->begin_reversible_command (_("Move Video"));
1742 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1743 ARDOUR_UI::instance()->video_timeline->save_undo();
1744 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1745 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1747 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1748 i->view->drag_end();
1749 i->view->fake_set_opaque (false);
1750 i->view->region()->resume_property_changes ();
1752 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1755 _editor->session()->maybe_update_session_range(
1756 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1757 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1761 _editor->commit_reversible_command ();
1762 _editor->update_canvas_now ();
1766 VideoTimeLineDrag::aborted (bool)
1768 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1771 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1772 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1774 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1775 i->view->region()->resume_property_changes ();
1776 i->view->region()->set_position(i->initial_position);
1781 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1782 : RegionDrag (e, i, p, v)
1784 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1785 _preserve_fade_anchor = preserve_fade_anchor;
1789 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1792 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1793 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1795 if (tv && tv->is_track()) {
1796 speed = tv->track()->speed();
1799 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1800 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1801 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1803 framepos_t const pf = adjusted_current_frame (event);
1805 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1806 /* Move the contents of the region around without changing the region bounds */
1807 _operation = ContentsTrim;
1808 Drag::start_grab (event, _editor->cursors()->trimmer);
1810 /* These will get overridden for a point trim.*/
1811 if (pf < (region_start + region_length/2)) {
1812 /* closer to front */
1813 _operation = StartTrim;
1814 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1817 _operation = EndTrim;
1818 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1822 switch (_operation) {
1824 show_verbose_cursor_time (region_start);
1825 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1826 i->view->trim_front_starting ();
1830 show_verbose_cursor_time (region_end);
1833 show_verbose_cursor_time (pf);
1837 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1838 i->view->region()->suspend_property_changes ();
1843 TrimDrag::motion (GdkEvent* event, bool first_move)
1845 RegionView* rv = _primary;
1848 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1849 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1850 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1852 if (tv && tv->is_track()) {
1853 speed = tv->track()->speed();
1856 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1862 switch (_operation) {
1864 trim_type = "Region start trim";
1867 trim_type = "Region end trim";
1870 trim_type = "Region content trim";
1874 _editor->begin_reversible_command (trim_type);
1876 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1877 RegionView* rv = i->view;
1878 rv->fake_set_opaque (false);
1879 rv->enable_display (false);
1880 rv->region()->playlist()->clear_owned_changes ();
1882 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1885 arv->temporarily_hide_envelope ();
1889 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1890 insert_result = _editor->motion_frozen_playlists.insert (pl);
1892 if (insert_result.second) {
1898 bool non_overlap_trim = false;
1900 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1901 non_overlap_trim = true;
1904 switch (_operation) {
1906 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1907 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1908 if (changed && _preserve_fade_anchor) {
1909 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1914 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1915 distance = _drags->current_pointer_x() - grab_x();
1916 len = ar->fade_in()->back()->when;
1917 new_length = len - _editor->unit_to_frame (distance);
1918 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
1919 arv->reset_fade_in_shape_width (ar, new_length); //the grey shape
1926 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1927 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1928 if (changed && _preserve_fade_anchor) {
1929 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1934 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1935 distance = grab_x() - _drags->current_pointer_x();
1936 len = ar->fade_out()->back()->when;
1937 new_length = len - _editor->unit_to_frame (distance);
1938 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
1939 arv->reset_fade_out_shape_width (ar, new_length); //the grey shape
1947 bool swap_direction = false;
1949 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1950 swap_direction = true;
1953 framecnt_t frame_delta = 0;
1955 bool left_direction = false;
1956 if (last_pointer_frame() > adjusted_current_frame(event)) {
1957 left_direction = true;
1960 if (left_direction) {
1961 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1963 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1966 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1967 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1973 switch (_operation) {
1975 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1978 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1981 show_verbose_cursor_time (adjusted_current_frame (event));
1988 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1990 if (movement_occurred) {
1991 motion (event, false);
1993 if (_operation == StartTrim) {
1994 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1996 /* This must happen before the region's StatefulDiffCommand is created, as it may
1997 `correct' (ahem) the region's _start from being negative to being zero. It
1998 needs to be zero in the undo record.
2000 i->view->trim_front_ending ();
2002 if (_preserve_fade_anchor) {
2003 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2008 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2009 distance = _drags->current_pointer_x() - grab_x();
2010 len = ar->fade_in()->back()->when;
2011 new_length = len - _editor->unit_to_frame (distance);
2012 new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2013 ar->set_fade_in_length(new_length);
2017 } else if (_operation == EndTrim) {
2018 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2019 if (_preserve_fade_anchor) {
2020 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2025 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2026 distance = _drags->current_pointer_x() - grab_x();
2027 len = ar->fade_out()->back()->when;
2028 new_length = len - _editor->unit_to_frame (distance);
2029 new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2030 ar->set_fade_out_length(new_length);
2036 if (!_views.empty()) {
2037 if (_operation == StartTrim) {
2038 _editor->maybe_locate_with_edit_preroll(
2039 _views.begin()->view->region()->position());
2041 if (_operation == EndTrim) {
2042 _editor->maybe_locate_with_edit_preroll(
2043 _views.begin()->view->region()->position() +
2044 _views.begin()->view->region()->length());
2048 if (!_editor->selection->selected (_primary)) {
2049 _primary->thaw_after_trim ();
2052 set<boost::shared_ptr<Playlist> > diffed_playlists;
2054 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2055 i->view->thaw_after_trim ();
2056 i->view->enable_display (true);
2057 i->view->fake_set_opaque (true);
2059 /* Trimming one region may affect others on the playlist, so we need
2060 to get undo Commands from the whole playlist rather than just the
2061 region. Use diffed_playlists to make sure we don't diff a given
2062 playlist more than once.
2064 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2065 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2066 vector<Command*> cmds;
2068 _editor->session()->add_commands (cmds);
2069 diffed_playlists.insert (p);
2074 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2078 _editor->motion_frozen_playlists.clear ();
2079 _editor->commit_reversible_command();
2082 /* no mouse movement */
2083 _editor->point_trim (event, adjusted_current_frame (event));
2086 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2087 if (_operation == StartTrim) {
2088 i->view->trim_front_ending ();
2091 i->view->region()->resume_property_changes ();
2096 TrimDrag::aborted (bool movement_occurred)
2098 /* Our motion method is changing model state, so use the Undo system
2099 to cancel. Perhaps not ideal, as this will leave an Undo point
2100 behind which may be slightly odd from the user's point of view.
2105 if (movement_occurred) {
2109 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2110 i->view->region()->resume_property_changes ();
2115 TrimDrag::setup_pointer_frame_offset ()
2117 list<DraggingView>::iterator i = _views.begin ();
2118 while (i != _views.end() && i->view != _primary) {
2122 if (i == _views.end()) {
2126 switch (_operation) {
2128 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2131 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2138 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2142 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2143 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2148 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2150 Drag::start_grab (event, cursor);
2151 show_verbose_cursor_time (adjusted_current_frame(event));
2155 MeterMarkerDrag::setup_pointer_frame_offset ()
2157 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2161 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2165 // create a dummy marker for visual representation of moving the
2166 // section, because whether its a copy or not, we're going to
2167 // leave or lose the original marker (leave if its a copy; lose if its
2168 // not, because we'll remove it from the map).
2170 MeterSection section (_marker->meter());
2172 if (!section.movable()) {
2177 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2179 _marker = new MeterMarker (
2181 *_editor->meter_group,
2182 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
2184 *new MeterSection (_marker->meter())
2187 /* use the new marker for the grab */
2188 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2191 TempoMap& map (_editor->session()->tempo_map());
2192 /* get current state */
2193 before_state = &map.get_state();
2194 /* remove the section while we drag it */
2195 map.remove_meter (section, true);
2199 framepos_t const pf = adjusted_current_frame (event);
2200 _marker->set_position (pf);
2201 show_verbose_cursor_time (pf);
2205 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2207 if (!movement_occurred) {
2211 motion (event, false);
2213 Timecode::BBT_Time when;
2215 TempoMap& map (_editor->session()->tempo_map());
2216 map.bbt_time (last_pointer_frame(), when);
2218 if (_copy == true) {
2219 _editor->begin_reversible_command (_("copy meter mark"));
2220 XMLNode &before = map.get_state();
2221 map.add_meter (_marker->meter(), when);
2222 XMLNode &after = map.get_state();
2223 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2224 _editor->commit_reversible_command ();
2227 _editor->begin_reversible_command (_("move meter mark"));
2229 /* we removed it before, so add it back now */
2231 map.add_meter (_marker->meter(), when);
2232 XMLNode &after = map.get_state();
2233 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2234 _editor->commit_reversible_command ();
2237 // delete the dummy marker we used for visual representation while moving.
2238 // a new visual marker will show up automatically.
2243 MeterMarkerDrag::aborted (bool moved)
2245 _marker->set_position (_marker->meter().frame ());
2248 TempoMap& map (_editor->session()->tempo_map());
2249 /* we removed it before, so add it back now */
2250 map.add_meter (_marker->meter(), _marker->meter().frame());
2251 // delete the dummy marker we used for visual representation while moving.
2252 // a new visual marker will show up automatically.
2257 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2261 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2263 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2268 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2270 Drag::start_grab (event, cursor);
2271 show_verbose_cursor_time (adjusted_current_frame (event));
2275 TempoMarkerDrag::setup_pointer_frame_offset ()
2277 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2281 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2285 // create a dummy marker for visual representation of moving the
2286 // section, because whether its a copy or not, we're going to
2287 // leave or lose the original marker (leave if its a copy; lose if its
2288 // not, because we'll remove it from the map).
2290 // create a dummy marker for visual representation of moving the copy.
2291 // The actual copying is not done before we reach the finish callback.
2294 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2296 TempoSection section (_marker->tempo());
2298 _marker = new TempoMarker (
2300 *_editor->tempo_group,
2301 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
2303 *new TempoSection (_marker->tempo())
2306 /* use the new marker for the grab */
2307 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2310 TempoMap& map (_editor->session()->tempo_map());
2311 /* get current state */
2312 before_state = &map.get_state();
2313 /* remove the section while we drag it */
2314 map.remove_tempo (section, true);
2318 framepos_t const pf = adjusted_current_frame (event);
2319 _marker->set_position (pf);
2320 show_verbose_cursor_time (pf);
2324 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2326 if (!movement_occurred) {
2330 motion (event, false);
2332 TempoMap& map (_editor->session()->tempo_map());
2333 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2334 Timecode::BBT_Time when;
2336 map.bbt_time (beat_time, when);
2338 if (_copy == true) {
2339 _editor->begin_reversible_command (_("copy tempo mark"));
2340 XMLNode &before = map.get_state();
2341 map.add_tempo (_marker->tempo(), when);
2342 XMLNode &after = map.get_state();
2343 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2344 _editor->commit_reversible_command ();
2347 _editor->begin_reversible_command (_("move tempo mark"));
2348 /* we removed it before, so add it back now */
2349 map.add_tempo (_marker->tempo(), when);
2350 XMLNode &after = map.get_state();
2351 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2352 _editor->commit_reversible_command ();
2355 // delete the dummy marker we used for visual representation while moving.
2356 // a new visual marker will show up automatically.
2361 TempoMarkerDrag::aborted (bool moved)
2363 _marker->set_position (_marker->tempo().frame());
2365 TempoMap& map (_editor->session()->tempo_map());
2366 /* we removed it before, so add it back now */
2367 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2368 // delete the dummy marker we used for visual representation while moving.
2369 // a new visual marker will show up automatically.
2374 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2378 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2381 /** Do all the things we do when dragging the playhead to make it look as though
2382 * we have located, without actually doing the locate (because that would cause
2383 * the diskstream buffers to be refilled, which is too slow).
2386 CursorDrag::fake_locate (framepos_t t)
2388 _editor->playhead_cursor->set_position (t);
2390 Session* s = _editor->session ();
2391 if (s->timecode_transmission_suspended ()) {
2392 framepos_t const f = _editor->playhead_cursor->current_frame;
2393 s->send_mmc_locate (f);
2394 s->send_full_time_code (f);
2397 show_verbose_cursor_time (t);
2398 _editor->UpdateAllTransportClocks (t);
2402 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2404 Drag::start_grab (event, c);
2406 _grab_zoom = _editor->frames_per_unit;
2408 framepos_t where = _editor->event_frame (event, 0, 0);
2409 _editor->snap_to_with_modifier (where, event);
2411 _editor->_dragging_playhead = true;
2413 Session* s = _editor->session ();
2416 if (_was_rolling && _stop) {
2420 if (s->is_auditioning()) {
2421 s->cancel_audition ();
2425 if (AudioEngine::instance()->connected()) {
2427 /* do this only if we're the engine is connected
2428 * because otherwise this request will never be
2429 * serviced and we'll busy wait forever. likewise,
2430 * notice if we are disconnected while waiting for the
2431 * request to be serviced.
2434 s->request_suspend_timecode_transmission ();
2435 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2436 /* twiddle our thumbs */
2441 fake_locate (where);
2445 CursorDrag::motion (GdkEvent* event, bool)
2447 framepos_t const adjusted_frame = adjusted_current_frame (event);
2448 if (adjusted_frame != last_pointer_frame()) {
2449 fake_locate (adjusted_frame);
2451 _editor->update_canvas_now ();
2457 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2459 _editor->_dragging_playhead = false;
2461 if (!movement_occurred && _stop) {
2465 motion (event, false);
2467 Session* s = _editor->session ();
2469 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2470 _editor->_pending_locate_request = true;
2471 s->request_resume_timecode_transmission ();
2476 CursorDrag::aborted (bool)
2478 if (_editor->_dragging_playhead) {
2479 _editor->session()->request_resume_timecode_transmission ();
2480 _editor->_dragging_playhead = false;
2483 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2486 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2487 : RegionDrag (e, i, p, v)
2489 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2493 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2495 Drag::start_grab (event, cursor);
2497 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2498 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2500 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2504 FadeInDrag::setup_pointer_frame_offset ()
2506 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2507 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2508 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2512 FadeInDrag::motion (GdkEvent* event, bool)
2514 framecnt_t fade_length;
2516 framepos_t const pos = adjusted_current_frame (event);
2518 boost::shared_ptr<Region> region = _primary->region ();
2520 if (pos < (region->position() + 64)) {
2521 fade_length = 64; // this should be a minimum defined somewhere
2522 } else if (pos > region->last_frame()) {
2523 fade_length = region->length();
2525 fade_length = pos - region->position();
2528 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2530 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2536 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2539 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2543 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2545 if (!movement_occurred) {
2549 framecnt_t fade_length;
2551 framepos_t const pos = adjusted_current_frame (event);
2553 boost::shared_ptr<Region> region = _primary->region ();
2555 if (pos < (region->position() + 64)) {
2556 fade_length = 64; // this should be a minimum defined somewhere
2557 } else if (pos > region->last_frame()) {
2558 fade_length = region->length();
2560 fade_length = pos - region->position();
2563 _editor->begin_reversible_command (_("change fade in length"));
2565 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2567 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2573 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2574 XMLNode &before = alist->get_state();
2576 tmp->audio_region()->set_fade_in_length (fade_length);
2577 tmp->audio_region()->set_fade_in_active (true);
2579 XMLNode &after = alist->get_state();
2580 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2583 _editor->commit_reversible_command ();
2587 FadeInDrag::aborted (bool)
2589 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2590 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2596 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2600 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2601 : RegionDrag (e, i, p, v)
2603 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2607 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2609 Drag::start_grab (event, cursor);
2611 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2612 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2614 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2618 FadeOutDrag::setup_pointer_frame_offset ()
2620 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2621 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2622 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2626 FadeOutDrag::motion (GdkEvent* event, bool)
2628 framecnt_t fade_length;
2630 framepos_t const pos = adjusted_current_frame (event);
2632 boost::shared_ptr<Region> region = _primary->region ();
2634 if (pos > (region->last_frame() - 64)) {
2635 fade_length = 64; // this should really be a minimum fade defined somewhere
2637 else if (pos < region->position()) {
2638 fade_length = region->length();
2641 fade_length = region->last_frame() - pos;
2644 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2646 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2652 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2655 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2659 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2661 if (!movement_occurred) {
2665 framecnt_t fade_length;
2667 framepos_t const pos = adjusted_current_frame (event);
2669 boost::shared_ptr<Region> region = _primary->region ();
2671 if (pos > (region->last_frame() - 64)) {
2672 fade_length = 64; // this should really be a minimum fade defined somewhere
2674 else if (pos < region->position()) {
2675 fade_length = region->length();
2678 fade_length = region->last_frame() - pos;
2681 _editor->begin_reversible_command (_("change fade out length"));
2683 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2685 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2691 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2692 XMLNode &before = alist->get_state();
2694 tmp->audio_region()->set_fade_out_length (fade_length);
2695 tmp->audio_region()->set_fade_out_active (true);
2697 XMLNode &after = alist->get_state();
2698 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2701 _editor->commit_reversible_command ();
2705 FadeOutDrag::aborted (bool)
2707 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2708 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2714 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2718 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2721 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2723 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2726 _points.push_back (Gnome::Art::Point (0, 0));
2727 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2730 MarkerDrag::~MarkerDrag ()
2732 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2737 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2739 location = new Location (*l);
2740 markers.push_back (m);
2745 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2747 Drag::start_grab (event, cursor);
2751 Location *location = _editor->find_location_from_marker (_marker, is_start);
2752 _editor->_dragging_edit_point = true;
2754 update_item (location);
2756 // _drag_line->show();
2757 // _line->raise_to_top();
2760 show_verbose_cursor_time (location->start());
2762 show_verbose_cursor_time (location->end());
2765 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2768 case Selection::Toggle:
2769 /* we toggle on the button release */
2771 case Selection::Set:
2772 if (!_editor->selection->selected (_marker)) {
2773 _editor->selection->set (_marker);
2776 case Selection::Extend:
2778 Locations::LocationList ll;
2779 list<Marker*> to_add;
2781 _editor->selection->markers.range (s, e);
2782 s = min (_marker->position(), s);
2783 e = max (_marker->position(), e);
2786 if (e < max_framepos) {
2789 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2790 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2791 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2794 to_add.push_back (lm->start);
2797 to_add.push_back (lm->end);
2801 if (!to_add.empty()) {
2802 _editor->selection->add (to_add);
2806 case Selection::Add:
2807 _editor->selection->add (_marker);
2811 /* Set up copies for us to manipulate during the drag
2814 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2816 Location* l = _editor->find_location_from_marker (*i, is_start);
2823 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2825 /* range: check that the other end of the range isn't
2828 CopiedLocationInfo::iterator x;
2829 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2830 if (*(*x).location == *l) {
2834 if (x == _copied_locations.end()) {
2835 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2837 (*x).markers.push_back (*i);
2838 (*x).move_both = true;
2846 MarkerDrag::setup_pointer_frame_offset ()
2849 Location *location = _editor->find_location_from_marker (_marker, is_start);
2850 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2854 MarkerDrag::motion (GdkEvent* event, bool)
2856 framecnt_t f_delta = 0;
2858 bool move_both = false;
2859 Location *real_location;
2860 Location *copy_location = 0;
2862 framepos_t const newframe = adjusted_current_frame (event);
2863 framepos_t next = newframe;
2865 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2869 CopiedLocationInfo::iterator x;
2871 /* find the marker we're dragging, and compute the delta */
2873 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2875 copy_location = (*x).location;
2877 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2879 /* this marker is represented by this
2880 * CopiedLocationMarkerInfo
2883 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
2888 if (real_location->is_mark()) {
2889 f_delta = newframe - copy_location->start();
2893 switch (_marker->type()) {
2894 case Marker::SessionStart:
2895 case Marker::RangeStart:
2896 case Marker::LoopStart:
2897 case Marker::PunchIn:
2898 f_delta = newframe - copy_location->start();
2901 case Marker::SessionEnd:
2902 case Marker::RangeEnd:
2903 case Marker::LoopEnd:
2904 case Marker::PunchOut:
2905 f_delta = newframe - copy_location->end();
2908 /* what kind of marker is this ? */
2917 if (x == _copied_locations.end()) {
2918 /* hmm, impossible - we didn't find the dragged marker */
2922 /* now move them all */
2924 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2926 copy_location = x->location;
2928 /* call this to find out if its the start or end */
2930 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
2934 if (real_location->locked()) {
2938 if (copy_location->is_mark()) {
2942 copy_location->set_start (copy_location->start() + f_delta);
2946 framepos_t new_start = copy_location->start() + f_delta;
2947 framepos_t new_end = copy_location->end() + f_delta;
2949 if (is_start) { // start-of-range marker
2951 if (move_both || (*x).move_both) {
2952 copy_location->set_start (new_start);
2953 copy_location->set_end (new_end);
2954 } else if (new_start < copy_location->end()) {
2955 copy_location->set_start (new_start);
2956 } else if (newframe > 0) {
2957 _editor->snap_to (next, 1, true);
2958 copy_location->set_end (next);
2959 copy_location->set_start (newframe);
2962 } else { // end marker
2964 if (move_both || (*x).move_both) {
2965 copy_location->set_end (new_end);
2966 copy_location->set_start (new_start);
2967 } else if (new_end > copy_location->start()) {
2968 copy_location->set_end (new_end);
2969 } else if (newframe > 0) {
2970 _editor->snap_to (next, -1, true);
2971 copy_location->set_start (next);
2972 copy_location->set_end (newframe);
2977 update_item (copy_location);
2979 /* now lookup the actual GUI items used to display this
2980 * location and move them to wherever the copy of the location
2981 * is now. This means that the logic in ARDOUR::Location is
2982 * still enforced, even though we are not (yet) modifying
2983 * the real Location itself.
2986 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2989 lm->set_position (copy_location->start(), copy_location->end());
2994 assert (!_copied_locations.empty());
2996 show_verbose_cursor_time (newframe);
2999 _editor->update_canvas_now ();
3004 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3006 if (!movement_occurred) {
3008 /* just a click, do nothing but finish
3009 off the selection process
3012 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3015 case Selection::Set:
3016 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3017 _editor->selection->set (_marker);
3021 case Selection::Toggle:
3022 /* we toggle on the button release, click only */
3023 _editor->selection->toggle (_marker);
3026 case Selection::Extend:
3027 case Selection::Add:
3034 _editor->_dragging_edit_point = false;
3036 _editor->begin_reversible_command ( _("move marker") );
3037 XMLNode &before = _editor->session()->locations()->get_state();
3039 MarkerSelection::iterator i;
3040 CopiedLocationInfo::iterator x;
3043 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3044 x != _copied_locations.end() && i != _editor->selection->markers.end();
3047 Location * location = _editor->find_location_from_marker (*i, is_start);
3051 if (location->locked()) {
3055 if (location->is_mark()) {
3056 location->set_start (((*x).location)->start());
3058 location->set (((*x).location)->start(), ((*x).location)->end());
3063 XMLNode &after = _editor->session()->locations()->get_state();
3064 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3065 _editor->commit_reversible_command ();
3069 MarkerDrag::aborted (bool)
3075 MarkerDrag::update_item (Location*)
3080 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3082 _cumulative_x_drag (0),
3083 _cumulative_y_drag (0)
3085 if (_zero_gain_fraction < 0.0) {
3086 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3089 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3091 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3097 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3099 Drag::start_grab (event, _editor->cursors()->fader);
3101 // start the grab at the center of the control point so
3102 // the point doesn't 'jump' to the mouse after the first drag
3103 _fixed_grab_x = _point->get_x();
3104 _fixed_grab_y = _point->get_y();
3106 float const fraction = 1 - (_point->get_y() / _point->line().height());
3108 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3110 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3111 event->button.x + 10, event->button.y + 10);
3113 _editor->verbose_cursor()->show ();
3115 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3117 if (!_point->can_slide ()) {
3118 _x_constrained = true;
3123 ControlPointDrag::motion (GdkEvent* event, bool)
3125 double dx = _drags->current_pointer_x() - last_pointer_x();
3126 double dy = _drags->current_pointer_y() - last_pointer_y();
3128 if (event->button.state & Keyboard::SecondaryModifier) {
3133 /* coordinate in pixels relative to the start of the region (for region-based automation)
3134 or track (for track-based automation) */
3135 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3136 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3138 // calculate zero crossing point. back off by .01 to stay on the
3139 // positive side of zero
3140 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3142 // make sure we hit zero when passing through
3143 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3147 if (_x_constrained) {
3150 if (_y_constrained) {
3154 _cumulative_x_drag = cx - _fixed_grab_x;
3155 _cumulative_y_drag = cy - _fixed_grab_y;
3159 cy = min ((double) _point->line().height(), cy);
3161 framepos_t cx_frames = _editor->unit_to_frame (cx);
3163 if (!_x_constrained) {
3164 _editor->snap_to_with_modifier (cx_frames, event);
3167 cx_frames = min (cx_frames, _point->line().maximum_time());
3169 float const fraction = 1.0 - (cy / _point->line().height());
3171 _point->line().drag_motion (_editor->frame_to_unit_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3173 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3177 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3179 if (!movement_occurred) {
3183 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3184 _editor->reset_point_selection ();
3188 motion (event, false);
3191 _point->line().end_drag (_pushing, _final_index);
3192 _editor->session()->commit_reversible_command ();
3196 ControlPointDrag::aborted (bool)
3198 _point->line().reset ();
3202 ControlPointDrag::active (Editing::MouseMode m)
3204 if (m == Editing::MouseGain) {
3205 /* always active in mouse gain */
3209 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3210 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3213 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3216 _cumulative_y_drag (0)
3218 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3222 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3224 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3227 _item = &_line->grab_item ();
3229 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3230 origin, and ditto for y.
3233 double cx = event->button.x;
3234 double cy = event->button.y;
3236 _line->parent_group().w2i (cx, cy);
3238 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
3243 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3244 /* no adjacent points */
3248 Drag::start_grab (event, _editor->cursors()->fader);
3250 /* store grab start in parent frame */
3255 double fraction = 1.0 - (cy / _line->height());
3257 _line->start_drag_line (before, after, fraction);
3259 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3260 event->button.x + 10, event->button.y + 10);
3262 _editor->verbose_cursor()->show ();
3266 LineDrag::motion (GdkEvent* event, bool)
3268 double dy = _drags->current_pointer_y() - last_pointer_y();
3270 if (event->button.state & Keyboard::SecondaryModifier) {
3274 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3276 _cumulative_y_drag = cy - _fixed_grab_y;
3279 cy = min ((double) _line->height(), cy);
3281 double const fraction = 1.0 - (cy / _line->height());
3284 /* we are ignoring x position for this drag, so we can just pass in anything */
3285 _line->drag_motion (0, fraction, true, false, ignored);
3287 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3291 LineDrag::finished (GdkEvent* event, bool)
3293 motion (event, false);
3294 _line->end_drag (false, 0);
3295 _editor->session()->commit_reversible_command ();
3299 LineDrag::aborted (bool)
3304 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3307 _cumulative_x_drag (0)
3309 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3313 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3315 Drag::start_grab (event);
3317 _line = reinterpret_cast<Line*> (_item);
3320 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3322 double cx = event->button.x;
3323 double cy = event->button.y;
3325 _item->property_parent().get_value()->w2i(cx, cy);
3327 /* store grab start in parent frame */
3328 _region_view_grab_x = cx;
3330 _before = *(float*) _item->get_data ("position");
3332 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3334 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3338 FeatureLineDrag::motion (GdkEvent*, bool)
3340 double dx = _drags->current_pointer_x() - last_pointer_x();
3342 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3344 _cumulative_x_drag += dx;
3346 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3355 ArdourCanvas::Points points;
3357 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3359 _line->get_bounds(x1, y2, x2, y2);
3361 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3362 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3364 _line->property_points() = points;
3366 float *pos = new float;
3369 _line->set_data ("position", pos);
3375 FeatureLineDrag::finished (GdkEvent*, bool)
3377 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3378 _arv->update_transient(_before, _before);
3382 FeatureLineDrag::aborted (bool)
3387 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3389 , _vertical_only (false)
3391 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3395 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3397 Drag::start_grab (event);
3398 show_verbose_cursor_time (adjusted_current_frame (event));
3402 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3409 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3411 framepos_t grab = grab_frame ();
3412 if (Config->get_rubberbanding_snaps_to_grid ()) {
3413 _editor->snap_to_with_modifier (grab, event);
3416 /* base start and end on initial click position */
3426 if (_drags->current_pointer_y() < grab_y()) {
3427 y1 = _drags->current_pointer_y();
3430 y2 = _drags->current_pointer_y();
3435 if (start != end || y1 != y2) {
3437 double x1 = _editor->frame_to_pixel (start);
3438 double x2 = _editor->frame_to_pixel (end);
3440 _editor->rubberband_rect->property_x1() = x1;
3441 if (_vertical_only) {
3442 /* fixed 10 pixel width */
3443 _editor->rubberband_rect->property_x2() = x1 + 10;
3445 _editor->rubberband_rect->property_x2() = x2;
3448 _editor->rubberband_rect->property_y1() = y1;
3449 _editor->rubberband_rect->property_y2() = y2;
3451 _editor->rubberband_rect->show();
3452 _editor->rubberband_rect->raise_to_top();
3454 show_verbose_cursor_time (pf);
3456 do_select_things (event, true);
3461 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3466 if (grab_frame() < last_pointer_frame()) {
3468 x2 = last_pointer_frame ();
3471 x1 = last_pointer_frame ();
3477 if (_drags->current_pointer_y() < grab_y()) {
3478 y1 = _drags->current_pointer_y();
3481 y2 = _drags->current_pointer_y();
3485 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3489 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3491 if (movement_occurred) {
3493 motion (event, false);
3494 do_select_things (event, false);
3500 bool do_deselect = true;
3501 MidiTimeAxisView* mtv;
3503 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3505 if (_editor->selection->empty()) {
3506 /* nothing selected */
3507 add_midi_region (mtv);
3508 do_deselect = false;
3512 /* do not deselect if Primary or Tertiary (toggle-select or
3513 * extend-select are pressed.
3516 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3517 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3524 _editor->rubberband_rect->hide();
3528 RubberbandSelectDrag::aborted (bool)
3530 _editor->rubberband_rect->hide ();
3533 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3534 : RegionDrag (e, i, p, v)
3536 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3540 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3542 Drag::start_grab (event, cursor);
3544 show_verbose_cursor_time (adjusted_current_frame (event));
3548 TimeFXDrag::motion (GdkEvent* event, bool)
3550 RegionView* rv = _primary;
3551 StreamView* cv = rv->get_time_axis_view().view ();
3553 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3554 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3555 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3557 framepos_t const pf = adjusted_current_frame (event);
3559 if (pf > rv->region()->position()) {
3560 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3563 show_verbose_cursor_time (pf);
3567 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3569 _primary->get_time_axis_view().hide_timestretch ();
3571 if (!movement_occurred) {
3575 if (last_pointer_frame() < _primary->region()->position()) {
3576 /* backwards drag of the left edge - not usable */
3580 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3582 float percentage = (double) newlen / (double) _primary->region()->length();
3584 #ifndef USE_RUBBERBAND
3585 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3586 if (_primary->region()->data_type() == DataType::AUDIO) {
3587 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3591 if (!_editor->get_selection().regions.empty()) {
3592 /* primary will already be included in the selection, and edit
3593 group shared editing will propagate selection across
3594 equivalent regions, so just use the current region
3598 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3599 error << _("An error occurred while executing time stretch operation") << endmsg;
3605 TimeFXDrag::aborted (bool)
3607 _primary->get_time_axis_view().hide_timestretch ();
3610 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3613 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3617 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3619 Drag::start_grab (event);
3623 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3625 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3629 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3631 if (movement_occurred && _editor->session()) {
3632 /* make sure we stop */
3633 _editor->session()->request_transport_speed (0.0);
3638 ScrubDrag::aborted (bool)
3643 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3648 , _original_pointer_time_axis (-1)
3649 , _last_pointer_time_axis (-1)
3650 , _time_selection_at_start (!_editor->get_selection().time.empty())
3652 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3654 if (_time_selection_at_start) {
3655 start_at_start = _editor->get_selection().time.start();
3656 end_at_start = _editor->get_selection().time.end_frame();
3661 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3663 if (_editor->session() == 0) {
3667 Gdk::Cursor* cursor = 0;
3669 switch (_operation) {
3670 case CreateSelection:
3671 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3676 cursor = _editor->cursors()->selector;
3677 Drag::start_grab (event, cursor);
3680 case SelectionStartTrim:
3681 if (_editor->clicked_axisview) {
3682 _editor->clicked_axisview->order_selection_trims (_item, true);
3684 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3687 case SelectionEndTrim:
3688 if (_editor->clicked_axisview) {
3689 _editor->clicked_axisview->order_selection_trims (_item, false);
3691 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3695 Drag::start_grab (event, cursor);
3698 case SelectionExtend:
3699 Drag::start_grab (event, cursor);
3703 if (_operation == SelectionMove) {
3704 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3706 show_verbose_cursor_time (adjusted_current_frame (event));
3709 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3713 SelectionDrag::setup_pointer_frame_offset ()
3715 switch (_operation) {
3716 case CreateSelection:
3717 _pointer_frame_offset = 0;
3720 case SelectionStartTrim:
3722 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3725 case SelectionEndTrim:
3726 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3729 case SelectionExtend:
3735 SelectionDrag::motion (GdkEvent* event, bool first_move)
3737 framepos_t start = 0;
3739 framecnt_t length = 0;
3740 framecnt_t distance = 0;
3742 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3743 if (pending_time_axis.first == 0) {
3747 framepos_t const pending_position = adjusted_current_frame (event);
3749 /* only alter selection if things have changed */
3751 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3755 switch (_operation) {
3756 case CreateSelection:
3758 framepos_t grab = grab_frame ();
3761 grab = adjusted_current_frame (event, false);
3762 if (grab < pending_position) {
3763 _editor->snap_to (grab, -1);
3765 _editor->snap_to (grab, 1);
3769 if (pending_position < grab) {
3770 start = pending_position;
3773 end = pending_position;
3777 /* first drag: Either add to the selection
3778 or create a new selection
3784 /* adding to the selection */
3785 _editor->set_selected_track_as_side_effect (Selection::Add);
3786 //_editor->selection->add (_editor->clicked_axisview);
3787 _editor->clicked_selection = _editor->selection->add (start, end);
3792 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3793 //_editor->selection->set (_editor->clicked_axisview);
3794 _editor->set_selected_track_as_side_effect (Selection::Set);
3797 _editor->clicked_selection = _editor->selection->set (start, end);
3801 /* select the track that we're in */
3802 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3803 // _editor->set_selected_track_as_side_effect (Selection::Add);
3804 _editor->selection->add (pending_time_axis.first);
3805 _added_time_axes.push_back (pending_time_axis.first);
3808 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3809 tracks that we selected in the first place.
3812 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3813 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3815 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3816 while (i != _added_time_axes.end()) {
3818 list<TimeAxisView*>::iterator tmp = i;
3821 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3822 _editor->selection->remove (*i);
3823 _added_time_axes.remove (*i);
3832 case SelectionStartTrim:
3834 start = _editor->selection->time[_editor->clicked_selection].start;
3835 end = _editor->selection->time[_editor->clicked_selection].end;
3837 if (pending_position > end) {
3840 start = pending_position;
3844 case SelectionEndTrim:
3846 start = _editor->selection->time[_editor->clicked_selection].start;
3847 end = _editor->selection->time[_editor->clicked_selection].end;
3849 if (pending_position < start) {
3852 end = pending_position;
3859 start = _editor->selection->time[_editor->clicked_selection].start;
3860 end = _editor->selection->time[_editor->clicked_selection].end;
3862 length = end - start;
3863 distance = pending_position - start;
3864 start = pending_position;
3865 _editor->snap_to (start);
3867 end = start + length;
3871 case SelectionExtend:
3875 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3876 _editor->start_canvas_autoscroll (1, 0);
3880 switch (_operation) {
3882 if (_time_selection_at_start) {
3883 _editor->selection->move_time (distance);
3887 _editor->selection->replace (_editor->clicked_selection, start, end);
3891 if (_operation == SelectionMove) {
3892 show_verbose_cursor_time(start);
3894 show_verbose_cursor_time(pending_position);
3899 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3901 Session* s = _editor->session();
3903 if (movement_occurred) {
3904 motion (event, false);
3905 /* XXX this is not object-oriented programming at all. ick */
3906 if (_editor->selection->time.consolidate()) {
3907 _editor->selection->TimeChanged ();
3910 /* XXX what if its a music time selection? */
3912 if ( s->get_play_range() && s->transport_rolling() ) {
3913 s->request_play_range (&_editor->selection->time, true);
3915 if (Config->get_always_play_range() && !s->transport_rolling()) {
3916 s->request_locate (_editor->get_selection().time.start());
3922 /* just a click, no pointer movement.
3925 if (_operation == SelectionExtend) {
3926 if (_time_selection_at_start) {
3927 framepos_t pos = adjusted_current_frame (event, false);
3928 framepos_t start = min (pos, start_at_start);
3929 framepos_t end = max (pos, end_at_start);
3930 _editor->selection->set (start, end);
3933 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3934 if (_editor->clicked_selection) {
3935 _editor->selection->remove (_editor->clicked_selection);
3938 if (!_editor->clicked_selection) {
3939 _editor->selection->clear_time();
3944 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3945 _editor->selection->set (_editor->clicked_axisview);
3948 if (s && s->get_play_range () && s->transport_rolling()) {
3949 s->request_stop (false, false);
3954 _editor->stop_canvas_autoscroll ();
3955 _editor->clicked_selection = 0;
3959 SelectionDrag::aborted (bool)
3964 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3969 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3971 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3972 physical_screen_height (_editor->get_window()));
3973 _drag_rect->hide ();
3975 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3976 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3980 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3982 if (_editor->session() == 0) {
3986 Gdk::Cursor* cursor = 0;
3988 if (!_editor->temp_location) {
3989 _editor->temp_location = new Location (*_editor->session());
3992 switch (_operation) {
3993 case CreateRangeMarker:
3994 case CreateTransportMarker:
3995 case CreateCDMarker:
3997 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4002 cursor = _editor->cursors()->selector;
4006 Drag::start_grab (event, cursor);
4008 show_verbose_cursor_time (adjusted_current_frame (event));
4012 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4014 framepos_t start = 0;
4016 ArdourCanvas::SimpleRect *crect;
4018 switch (_operation) {
4019 case CreateRangeMarker:
4020 crect = _editor->range_bar_drag_rect;
4022 case CreateTransportMarker:
4023 crect = _editor->transport_bar_drag_rect;
4025 case CreateCDMarker:
4026 crect = _editor->cd_marker_bar_drag_rect;
4029 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4034 framepos_t const pf = adjusted_current_frame (event);
4036 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4037 framepos_t grab = grab_frame ();
4038 _editor->snap_to (grab);
4040 if (pf < grab_frame()) {
4048 /* first drag: Either add to the selection
4049 or create a new selection.
4054 _editor->temp_location->set (start, end);
4058 update_item (_editor->temp_location);
4060 //_drag_rect->raise_to_top();
4065 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
4066 _editor->start_canvas_autoscroll (1, 0);
4070 _editor->temp_location->set (start, end);
4072 double x1 = _editor->frame_to_pixel (start);
4073 double x2 = _editor->frame_to_pixel (end);
4074 crect->property_x1() = x1;
4075 crect->property_x2() = x2;
4077 update_item (_editor->temp_location);
4080 show_verbose_cursor_time (pf);
4085 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4087 Location * newloc = 0;
4091 if (movement_occurred) {
4092 motion (event, false);
4095 switch (_operation) {
4096 case CreateRangeMarker:
4097 case CreateCDMarker:
4099 _editor->begin_reversible_command (_("new range marker"));
4100 XMLNode &before = _editor->session()->locations()->get_state();
4101 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4102 if (_operation == CreateCDMarker) {
4103 flags = Location::IsRangeMarker | Location::IsCDMarker;
4104 _editor->cd_marker_bar_drag_rect->hide();
4107 flags = Location::IsRangeMarker;
4108 _editor->range_bar_drag_rect->hide();
4110 newloc = new Location (
4111 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4114 _editor->session()->locations()->add (newloc, true);
4115 XMLNode &after = _editor->session()->locations()->get_state();
4116 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4117 _editor->commit_reversible_command ();
4121 case CreateTransportMarker:
4122 // popup menu to pick loop or punch
4123 _editor->new_transport_marker_context_menu (&event->button, _item);
4127 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4129 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
4134 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4136 if (end == max_framepos) {
4137 end = _editor->session()->current_end_frame ();
4140 if (start == max_framepos) {
4141 start = _editor->session()->current_start_frame ();
4144 switch (_editor->mouse_mode) {
4146 /* find the two markers on either side and then make the selection from it */
4147 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4151 /* find the two markers on either side of the click and make the range out of it */
4152 _editor->selection->set (start, end);
4161 _editor->stop_canvas_autoscroll ();
4165 RangeMarkerBarDrag::aborted (bool)
4171 RangeMarkerBarDrag::update_item (Location* location)
4173 double const x1 = _editor->frame_to_pixel (location->start());
4174 double const x2 = _editor->frame_to_pixel (location->end());
4176 _drag_rect->property_x1() = x1;
4177 _drag_rect->property_x2() = x2;
4180 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4184 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4188 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4190 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4191 Drag::start_grab (event, _editor->cursors()->zoom_out);
4194 Drag::start_grab (event, _editor->cursors()->zoom_in);
4198 show_verbose_cursor_time (adjusted_current_frame (event));
4202 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4207 framepos_t const pf = adjusted_current_frame (event);
4209 framepos_t grab = grab_frame ();
4210 _editor->snap_to_with_modifier (grab, event);
4212 /* base start and end on initial click position */
4224 _editor->zoom_rect->show();
4225 _editor->zoom_rect->raise_to_top();
4228 _editor->reposition_zoom_rect(start, end);
4230 show_verbose_cursor_time (pf);
4235 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4237 if (movement_occurred) {
4238 motion (event, false);
4240 if (grab_frame() < last_pointer_frame()) {
4241 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4243 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4246 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4247 _editor->tav_zoom_step (_zoom_out);
4249 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4253 _editor->zoom_rect->hide();
4257 MouseZoomDrag::aborted (bool)
4259 _editor->zoom_rect->hide ();
4262 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4264 , _cumulative_dx (0)
4265 , _cumulative_dy (0)
4267 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4269 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
4270 _region = &_primary->region_view ();
4271 _note_height = _region->midi_stream_view()->note_height ();
4275 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4277 Drag::start_grab (event);
4279 if (!(_was_selected = _primary->selected())) {
4281 /* tertiary-click means extend selection - we'll do that on button release,
4282 so don't add it here, because otherwise we make it hard to figure
4283 out the "extend-to" range.
4286 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4289 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4292 _region->note_selected (_primary, true);
4294 _region->unique_select (_primary);
4300 /** @return Current total drag x change in frames */
4302 NoteDrag::total_dx () const
4305 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
4307 /* primary note time */
4308 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4310 /* new time of the primary note in session frames */
4311 frameoffset_t st = n + dx;
4313 framepos_t const rp = _region->region()->position ();
4315 /* prevent the note being dragged earlier than the region's position */
4318 /* snap and return corresponding delta */
4319 return _region->snap_frame_to_frame (st - rp) + rp - n;
4322 /** @return Current total drag y change in note number */
4324 NoteDrag::total_dy () const
4326 MidiStreamView* msv = _region->midi_stream_view ();
4327 double const y = _region->midi_view()->y_position ();
4328 /* new current note */
4329 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4331 n = max (msv->lowest_note(), n);
4332 n = min (msv->highest_note(), n);
4333 /* and work out delta */
4334 return n - msv->y_to_note (grab_y() - y);
4338 NoteDrag::motion (GdkEvent *, bool)
4340 /* Total change in x and y since the start of the drag */
4341 frameoffset_t const dx = total_dx ();
4342 int8_t const dy = total_dy ();
4344 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4345 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
4346 double const tdy = -dy * _note_height - _cumulative_dy;
4349 _cumulative_dx += tdx;
4350 _cumulative_dy += tdy;
4352 int8_t note_delta = total_dy();
4354 _region->move_selection (tdx, tdy, note_delta);
4356 /* the new note value may be the same as the old one, but we
4357 * don't know what that means because the selection may have
4358 * involved more than one note and we might be doing something
4359 * odd with them. so show the note value anyway, always.
4363 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4365 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4366 (int) floor (new_note));
4368 show_verbose_cursor_text (buf);
4373 NoteDrag::finished (GdkEvent* ev, bool moved)
4376 /* no motion - select note */
4378 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4379 _editor->current_mouse_mode() == Editing::MouseDraw) {
4381 if (_was_selected) {
4382 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4384 _region->note_deselected (_primary);
4387 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4388 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4390 if (!extend && !add && _region->selection_size() > 1) {
4391 _region->unique_select (_primary);
4392 } else if (extend) {
4393 _region->note_selected (_primary, true, true);
4395 /* it was added during button press */
4400 _region->note_dropped (_primary, total_dx(), total_dy());
4405 NoteDrag::aborted (bool)
4410 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4411 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4412 : Drag (editor, atv->base_item ())
4414 , _nothing_to_drag (false)
4416 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4417 y_origin = atv->y_position();
4418 setup (atv->lines ());
4421 /** Make an AutomationRangeDrag for region gain lines */
4422 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4423 : Drag (editor, rv->get_canvas_group ())
4425 , _nothing_to_drag (false)
4427 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4429 list<boost::shared_ptr<AutomationLine> > lines;
4430 lines.push_back (rv->get_gain_line ());
4431 y_origin = rv->get_time_axis_view().y_position();
4435 /** @param lines AutomationLines to drag.
4436 * @param offset Offset from the session start to the points in the AutomationLines.
4439 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4441 /* find the lines that overlap the ranges being dragged */
4442 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4443 while (i != lines.end ()) {
4444 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4447 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4449 /* check this range against all the AudioRanges that we are using */
4450 list<AudioRange>::const_iterator k = _ranges.begin ();
4451 while (k != _ranges.end()) {
4452 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4458 /* add it to our list if it overlaps at all */
4459 if (k != _ranges.end()) {
4464 _lines.push_back (n);
4470 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4474 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4476 return 1.0 - ((global_y - y_origin) / line->height());
4480 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4482 Drag::start_grab (event, cursor);
4484 /* Get line states before we start changing things */
4485 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4486 i->state = &i->line->get_state ();
4487 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4490 if (_ranges.empty()) {
4492 /* No selected time ranges: drag all points */
4493 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4494 uint32_t const N = i->line->npoints ();
4495 for (uint32_t j = 0; j < N; ++j) {
4496 i->points.push_back (i->line->nth (j));
4502 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4504 framecnt_t const half = (i->start + i->end) / 2;
4506 /* find the line that this audio range starts in */
4507 list<Line>::iterator j = _lines.begin();
4508 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4512 if (j != _lines.end()) {
4513 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4515 /* j is the line that this audio range starts in; fade into it;
4516 64 samples length plucked out of thin air.
4519 framepos_t a = i->start + 64;
4524 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4525 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4527 the_list->add (p, the_list->eval (p));
4528 the_list->add (q, the_list->eval (q));
4531 /* same thing for the end */
4534 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4538 if (j != _lines.end()) {
4539 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4541 /* j is the line that this audio range starts in; fade out of it;
4542 64 samples length plucked out of thin air.
4545 framepos_t b = i->end - 64;
4550 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4551 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4553 the_list->add (p, the_list->eval (p));
4554 the_list->add (q, the_list->eval (q));
4558 _nothing_to_drag = true;
4560 /* Find all the points that should be dragged and put them in the relevant
4561 points lists in the Line structs.
4564 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4566 uint32_t const N = i->line->npoints ();
4567 for (uint32_t j = 0; j < N; ++j) {
4569 /* here's a control point on this line */
4570 ControlPoint* p = i->line->nth (j);
4571 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4573 /* see if it's inside a range */
4574 list<AudioRange>::const_iterator k = _ranges.begin ();
4575 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4579 if (k != _ranges.end()) {
4580 /* dragging this point */
4581 _nothing_to_drag = false;
4582 i->points.push_back (p);
4588 if (_nothing_to_drag) {
4592 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4593 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4598 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4600 if (_nothing_to_drag) {
4604 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4605 float const f = y_fraction (l->line, _drags->current_pointer_y());
4606 /* we are ignoring x position for this drag, so we can just pass in anything */
4608 l->line->drag_motion (0, f, true, false, ignored);
4609 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4614 AutomationRangeDrag::finished (GdkEvent* event, bool)
4616 if (_nothing_to_drag) {
4620 motion (event, false);
4621 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4622 i->line->end_drag (false, 0);
4625 _editor->session()->commit_reversible_command ();
4629 AutomationRangeDrag::aborted (bool)
4631 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4636 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4639 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4640 layer = v->region()->layer ();
4641 initial_y = v->get_canvas_group()->property_y ();
4642 initial_playlist = v->region()->playlist ();
4643 initial_position = v->region()->position ();
4644 initial_end = v->region()->position () + v->region()->length ();
4647 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4651 , _cumulative_dx (0)
4653 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4654 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4659 PatchChangeDrag::motion (GdkEvent* ev, bool)
4661 framepos_t f = adjusted_current_frame (ev);
4662 boost::shared_ptr<Region> r = _region_view->region ();
4663 f = max (f, r->position ());
4664 f = min (f, r->last_frame ());
4666 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4667 double const dxu = _editor->frame_to_unit (dxf); // permitted fx in units
4668 _patch_change->move (dxu - _cumulative_dx, 0);
4669 _cumulative_dx = dxu;
4673 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4675 if (!movement_occurred) {
4679 boost::shared_ptr<Region> r (_region_view->region ());
4680 framepos_t f = adjusted_current_frame (ev);
4681 f = max (f, r->position ());
4682 f = min (f, r->last_frame ());
4684 _region_view->move_patch_change (
4686 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4691 PatchChangeDrag::aborted (bool)
4693 _patch_change->move (-_cumulative_dx, 0);
4697 PatchChangeDrag::setup_pointer_frame_offset ()
4699 boost::shared_ptr<Region> region = _region_view->region ();
4700 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4703 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4704 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4711 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4713 framepos_t const p = _region_view->region()->position ();
4714 double const y = _region_view->midi_view()->y_position ();
4716 x1 = max ((framepos_t) 0, x1 - p);
4717 x2 = max ((framepos_t) 0, x2 - p);
4718 y1 = max (0.0, y1 - y);
4719 y2 = max (0.0, y2 - y);
4721 _region_view->update_drag_selection (
4722 _editor->frame_to_pixel (x1),
4723 _editor->frame_to_pixel (x2),
4726 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4731 MidiRubberbandSelectDrag::deselect_things ()
4736 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4737 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4740 _vertical_only = true;
4744 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4746 double const y = _region_view->midi_view()->y_position ();
4748 y1 = max (0.0, y1 - y);
4749 y2 = max (0.0, y2 - y);
4751 _region_view->update_vertical_drag_selection (
4754 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4759 MidiVerticalSelectDrag::deselect_things ()
4764 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4765 : RubberbandSelectDrag (e, i)
4771 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4773 if (drag_in_progress) {
4774 /* We just want to select things at the end of the drag, not during it */
4778 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4780 _editor->begin_reversible_command (_("rubberband selection"));
4781 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4782 _editor->commit_reversible_command ();
4786 EditorRubberbandSelectDrag::deselect_things ()
4788 if (!getenv("ARDOUR_SAE")) {
4789 _editor->selection->clear_tracks();
4791 _editor->selection->clear_regions();
4792 _editor->selection->clear_points ();
4793 _editor->selection->clear_lines ();
4796 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4804 NoteCreateDrag::~NoteCreateDrag ()
4810 NoteCreateDrag::grid_frames (framepos_t t) const
4813 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4818 return _region_view->region_beats_to_region_frames (grid_beats);
4822 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4824 Drag::start_grab (event, cursor);
4826 _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4828 framepos_t pf = _drags->current_pointer_frame ();
4829 framecnt_t const g = grid_frames (pf);
4831 /* Hack so that we always snap to the note that we are over, instead of snapping
4832 to the next one if we're more than halfway through the one we're over.
4834 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4838 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4840 MidiStreamView* sv = _region_view->midi_stream_view ();
4841 double const x = _editor->frame_to_pixel (_note[0]);
4842 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4844 _drag_rect->property_x1() = x;
4845 _drag_rect->property_y1() = y;
4846 _drag_rect->property_x2() = x;
4847 _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4849 _drag_rect->property_outline_what() = 0xff;
4850 _drag_rect->property_outline_color_rgba() = 0xffffff99;
4851 _drag_rect->property_fill_color_rgba() = 0xffffff66;
4855 NoteCreateDrag::motion (GdkEvent* event, bool)
4857 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4858 double const x = _editor->frame_to_pixel (_note[1]);
4859 if (_note[1] > _note[0]) {
4860 _drag_rect->property_x2() = x;
4862 _drag_rect->property_x1() = x;
4867 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4869 if (!had_movement) {
4873 framepos_t const start = min (_note[0], _note[1]);
4874 framecnt_t length = (framecnt_t) fabs (_note[0] - _note[1]);
4876 framecnt_t const g = grid_frames (start);
4877 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4879 if (_editor->snap_mode() == SnapNormal && length < g) {
4880 length = g - one_tick;
4883 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4885 _region_view->create_note_at (start, _drag_rect->property_y1(), length_beats, false);
4889 NoteCreateDrag::y_to_region (double y) const
4892 _region_view->get_canvas_group()->w2i (x, y);
4897 NoteCreateDrag::aborted (bool)
4902 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4907 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
4911 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4913 Drag::start_grab (event, cursor);
4917 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4923 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4926 distance = _drags->current_pointer_x() - grab_x();
4927 len = ar->fade_in()->back()->when;
4929 distance = grab_x() - _drags->current_pointer_x();
4930 len = ar->fade_out()->back()->when;
4933 /* how long should it be ? */
4935 new_length = len + _editor->unit_to_frame (distance);
4937 /* now check with the region that this is legal */
4939 new_length = ar->verify_xfade_bounds (new_length, start);
4942 arv->redraw_start_xfade_to (ar, new_length);
4944 arv->redraw_end_xfade_to (ar, new_length);
4949 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
4955 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4958 distance = _drags->current_pointer_x() - grab_x();
4959 len = ar->fade_in()->back()->when;
4961 distance = grab_x() - _drags->current_pointer_x();
4962 len = ar->fade_out()->back()->when;
4965 new_length = ar->verify_xfade_bounds (len + _editor->unit_to_frame (distance), start);
4967 _editor->begin_reversible_command ("xfade trim");
4968 ar->playlist()->clear_owned_changes ();
4971 ar->set_fade_in_length (new_length);
4973 ar->set_fade_out_length (new_length);
4976 /* Adjusting the xfade may affect other regions in the playlist, so we need
4977 to get undo Commands from the whole playlist rather than just the
4981 vector<Command*> cmds;
4982 ar->playlist()->rdiff (cmds);
4983 _editor->session()->add_commands (cmds);
4984 _editor->commit_reversible_command ();
4989 CrossfadeEdgeDrag::aborted (bool)
4992 arv->redraw_start_xfade ();
4994 arv->redraw_end_xfade ();