2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
26 #include "pbd/memento_command.h"
27 #include "pbd/basename.h"
28 #include "pbd/stateful_diff_command.h"
30 #include "gtkmm2ext/utils.h"
32 #include "ardour/session.h"
33 #include "ardour/dB.h"
34 #include "ardour/region_factory.h"
38 #include "audio_region_view.h"
39 #include "midi_region_view.h"
40 #include "ardour_ui.h"
41 #include "gui_thread.h"
42 #include "control_point.h"
44 #include "region_gain_line.h"
45 #include "editor_drag.h"
46 #include "audio_time_axis.h"
47 #include "midi_time_axis.h"
48 #include "canvas-note.h"
49 #include "selection.h"
50 #include "midi_selection.h"
51 #include "automation_time_axis.h"
53 #include "editor_cursors.h"
54 #include "mouse_cursors.h"
57 using namespace ARDOUR;
60 using namespace Gtkmm2ext;
61 using namespace Editing;
62 using namespace ArdourCanvas;
64 using Gtkmm2ext::Keyboard;
66 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
68 DragManager::DragManager (Editor* e)
71 , _current_pointer_frame (0)
76 DragManager::~DragManager ()
81 /** Call abort for each active drag */
87 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
98 DragManager::add (Drag* d)
100 d->set_manager (this);
101 _drags.push_back (d);
105 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
107 d->set_manager (this);
108 _drags.push_back (d);
113 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
115 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
117 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
118 (*i)->start_grab (e, c);
122 /** Call end_grab for each active drag.
123 * @return true if any drag reported movement having occurred.
126 DragManager::end_grab (GdkEvent* e)
131 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
132 bool const t = (*i)->end_grab (e);
147 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
151 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
153 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
154 bool const t = (*i)->motion_handler (e, from_autoscroll);
165 DragManager::have_item (ArdourCanvas::Item* i) const
167 list<Drag*>::const_iterator j = _drags.begin ();
168 while (j != _drags.end() && (*j)->item () != i) {
172 return j != _drags.end ();
175 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
178 , _pointer_frame_offset (0)
179 , _move_threshold_passed (false)
180 , _raw_grab_frame (0)
182 , _last_pointer_frame (0)
188 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
194 cursor = _editor->which_grabber_cursor ();
197 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
201 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
204 cursor = _editor->which_grabber_cursor ();
207 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
209 if (Keyboard::is_button2_event (&event->button)) {
210 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
211 _y_constrained = true;
212 _x_constrained = false;
214 _y_constrained = false;
215 _x_constrained = true;
218 _x_constrained = false;
219 _y_constrained = false;
222 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
223 _grab_frame = adjusted_frame (_raw_grab_frame, event);
224 _last_pointer_frame = _grab_frame;
225 _last_pointer_x = _grab_x;
226 _last_pointer_y = _grab_y;
228 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
232 if (_editor->session() && _editor->session()->transport_rolling()) {
235 _was_rolling = false;
238 switch (_editor->snap_type()) {
239 case SnapToRegionStart:
240 case SnapToRegionEnd:
241 case SnapToRegionSync:
242 case SnapToRegionBoundary:
243 _editor->build_region_boundary_cache ();
250 /** Call to end a drag `successfully'. Ungrabs item and calls
251 * subclass' finished() method.
253 * @param event GDK event, or 0.
254 * @return true if some movement occurred, otherwise false.
257 Drag::end_grab (GdkEvent* event)
259 _editor->stop_canvas_autoscroll ();
261 _item->ungrab (event ? event->button.time : 0);
263 finished (event, _move_threshold_passed);
265 _editor->hide_verbose_canvas_cursor();
267 return _move_threshold_passed;
271 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
275 if (f > _pointer_frame_offset) {
276 pos = f - _pointer_frame_offset;
280 _editor->snap_to_with_modifier (pos, event);
287 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
289 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
293 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
295 /* check to see if we have moved in any way that matters since the last motion event */
296 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
297 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
301 pair<framecnt_t, int> const threshold = move_threshold ();
303 bool const old_move_threshold_passed = _move_threshold_passed;
305 if (!from_autoscroll && !_move_threshold_passed) {
307 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
308 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
310 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
313 if (active (_editor->mouse_mode) && _move_threshold_passed) {
315 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
316 if (!from_autoscroll) {
317 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
320 motion (event, _move_threshold_passed != old_move_threshold_passed);
322 _last_pointer_x = _drags->current_pointer_x ();
323 _last_pointer_y = _drags->current_pointer_y ();
324 _last_pointer_frame = adjusted_current_frame (event);
332 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
342 _editor->stop_canvas_autoscroll ();
343 _editor->hide_verbose_canvas_cursor ();
346 struct EditorOrderTimeAxisViewSorter {
347 bool operator() (TimeAxisView* a, TimeAxisView* b) {
348 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
349 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
351 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
355 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
359 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
361 /* Make a list of non-hidden tracks to refer to during the drag */
363 TrackViewList track_views = _editor->track_views;
364 track_views.sort (EditorOrderTimeAxisViewSorter ());
366 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
367 if (!(*i)->hidden()) {
369 _time_axis_views.push_back (*i);
371 TimeAxisView::Children children_list = (*i)->get_child_list ();
372 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
373 _time_axis_views.push_back (j->get());
378 /* the list of views can be empty at this point if this is a region list-insert drag
381 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
382 _views.push_back (DraggingView (*i, this));
385 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
389 RegionDrag::region_going_away (RegionView* v)
391 list<DraggingView>::iterator i = _views.begin ();
392 while (i != _views.end() && i->view != v) {
396 if (i != _views.end()) {
401 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
403 RegionDrag::find_time_axis_view (TimeAxisView* t) const
406 int const N = _time_axis_views.size ();
407 while (i < N && _time_axis_views[i] != t) {
418 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
419 : RegionDrag (e, i, p, v),
428 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
430 Drag::start_grab (event, cursor);
432 _editor->show_verbose_time_cursor (_last_frame_position, 10);
434 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
435 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
436 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
440 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
442 /* compute the amount of pointer motion in frames, and where
443 the region would be if we moved it by that much.
445 *pending_region_position = adjusted_current_frame (event);
447 framepos_t sync_frame;
448 framecnt_t sync_offset;
451 sync_offset = _primary->region()->sync_offset (sync_dir);
453 /* we don't handle a sync point that lies before zero.
455 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
457 sync_frame = *pending_region_position + (sync_dir*sync_offset);
459 _editor->snap_to_with_modifier (sync_frame, event);
461 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
464 *pending_region_position = _last_frame_position;
467 if (*pending_region_position > max_framepos - _primary->region()->length()) {
468 *pending_region_position = _last_frame_position;
473 /* in locked edit mode, reverse the usual meaning of _x_constrained */
474 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
476 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
478 /* x movement since last time */
479 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
481 /* total x movement */
482 framecnt_t total_dx = *pending_region_position;
483 if (regions_came_from_canvas()) {
484 total_dx = total_dx - grab_frame () + _pointer_frame_offset;
487 /* check that no regions have gone off the start of the session */
488 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
489 if ((i->view->region()->position() + total_dx) < 0) {
491 *pending_region_position = _last_frame_position;
496 _last_frame_position = *pending_region_position;
503 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
505 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
506 int const n = i->time_axis_view + delta_track;
507 if (n < 0 || n >= int (_time_axis_views.size())) {
508 /* off the top or bottom track */
512 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
513 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
514 /* not a track, or the wrong type */
518 int const l = i->layer + delta_layer;
519 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
520 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
521 If it has, the layers will be munged later anyway, so it's ok.
527 /* all regions being dragged are ok with this change */
532 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
534 assert (!_views.empty ());
536 /* Find the TimeAxisView that the pointer is now over */
537 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
539 /* Bail early if we're not over a track */
540 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
541 if (!rtv || !rtv->is_track()) {
542 _editor->hide_verbose_canvas_cursor ();
546 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
548 /* Here's the current pointer position in terms of time axis view and layer */
549 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
550 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
552 /* Work out the change in x */
553 framepos_t pending_region_position;
554 double const x_delta = compute_x_delta (event, &pending_region_position);
556 /* Work out the change in y */
557 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
558 int delta_layer = current_pointer_layer - _last_pointer_layer;
560 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
561 /* this y movement is not allowed, so do no y movement this time */
562 delta_time_axis_view = 0;
566 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
567 /* haven't reached next snap point, and we're not switching
568 trackviews nor layers. nothing to do.
573 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
575 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
577 RegionView* rv = i->view;
579 if (rv->region()->locked()) {
585 /* here we are calculating the y distance from the
586 top of the first track view to the top of the region
587 area of the track view that we're working on */
589 /* this x value is just a dummy value so that we have something
594 /* distance from the top of this track view to the region area
595 of our track view is always 1 */
599 /* convert to world coordinates, ie distance from the top of
602 rv->get_canvas_frame()->i2w (ix1, iy1);
604 /* compensate for the ruler section and the vertical scrollbar position */
605 iy1 += _editor->get_trackview_group_vertical_offset ();
607 // hide any dependent views
609 rv->get_time_axis_view().hide_dependent_views (*rv);
612 reparent to a non scrolling group so that we can keep the
613 region selection above all time axis views.
614 reparenting means we have to move the rv as the two
615 parent groups have different coordinates.
618 rv->get_canvas_group()->property_y() = iy1 - 1;
619 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
621 rv->fake_set_opaque (true);
624 /* Work out the change in y position of this region view */
628 /* If we have moved tracks, we'll fudge the layer delta so that the
629 region gets moved back onto layer 0 on its new track; this avoids
630 confusion when dragging regions from non-zero layers onto different
633 int this_delta_layer = delta_layer;
634 if (delta_time_axis_view != 0) {
635 this_delta_layer = - i->layer;
638 /* Move this region to layer 0 on its old track */
639 StreamView* lv = _time_axis_views[i->time_axis_view]->view ();
640 if (lv->layer_display() == Stacked) {
641 y_delta -= (lv->layers() - i->layer - 1) * lv->child_height ();
644 /* Now move it to its right layer on the current track */
645 StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view ();
646 if (cv->layer_display() == Stacked) {
647 y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height ();
651 if (delta_time_axis_view > 0) {
652 for (int j = 0; j < delta_time_axis_view; ++j) {
653 y_delta += _time_axis_views[i->time_axis_view + j]->current_height ();
656 /* start by subtracting the height of the track above where we are now */
657 for (int j = 1; j <= -delta_time_axis_view; ++j) {
658 y_delta -= _time_axis_views[i->time_axis_view - j]->current_height ();
663 rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ());
665 /* Update the DraggingView */
666 i->time_axis_view += delta_time_axis_view;
667 i->layer += this_delta_layer;
670 _editor->mouse_brush_insert_region (rv, pending_region_position);
672 rv->move (x_delta, y_delta);
675 } /* foreach region */
677 _total_x_delta += x_delta;
680 _editor->cursor_group->raise_to_top();
683 if (x_delta != 0 && !_brushing) {
684 _editor->show_verbose_time_cursor (_last_frame_position, 10);
687 _last_pointer_time_axis_view += delta_time_axis_view;
688 _last_pointer_layer += delta_layer;
692 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
694 if (_copy && first_move) {
696 /* duplicate the regionview(s) and region(s) */
698 list<DraggingView> new_regionviews;
700 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
702 RegionView* rv = i->view;
703 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
704 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
706 const boost::shared_ptr<const Region> original = rv->region();
707 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
708 region_copy->set_position (original->position(), this);
712 boost::shared_ptr<AudioRegion> audioregion_copy
713 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
715 nrv = new AudioRegionView (*arv, audioregion_copy);
717 boost::shared_ptr<MidiRegion> midiregion_copy
718 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
719 nrv = new MidiRegionView (*mrv, midiregion_copy);
724 nrv->get_canvas_group()->show ();
725 new_regionviews.push_back (DraggingView (nrv, this));
727 /* swap _primary to the copy */
729 if (rv == _primary) {
733 /* ..and deselect the one we copied */
735 rv->set_selected (false);
738 if (!new_regionviews.empty()) {
740 /* reflect the fact that we are dragging the copies */
742 _views = new_regionviews;
744 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
747 sync the canvas to what we think is its current state
748 without it, the canvas seems to
749 "forget" to update properly after the upcoming reparent()
750 ..only if the mouse is in rapid motion at the time of the grab.
751 something to do with regionview creation taking so long?
753 _editor->update_canvas_now();
757 RegionMotionDrag::motion (event, first_move);
761 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
763 if (!movement_occurred) {
768 /* reverse this here so that we have the correct logic to finalize
772 if (Config->get_edit_mode() == Lock) {
773 _x_constrained = !_x_constrained;
776 assert (!_views.empty ());
778 bool const changed_position = (_last_frame_position != _primary->region()->position());
779 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
780 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
782 _editor->update_canvas_now ();
804 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
806 RegionSelection new_views;
807 PlaylistSet modified_playlists;
808 list<RegionView*> views_to_delete;
811 /* all changes were made during motion event handlers */
813 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
817 _editor->commit_reversible_command ();
821 if (_x_constrained) {
822 _editor->begin_reversible_command (_("fixed time region copy"));
824 _editor->begin_reversible_command (_("region copy"));
827 /* insert the regions into their new playlists */
828 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
830 if (i->view->region()->locked()) {
836 if (changed_position && !_x_constrained) {
837 where = i->view->region()->position() - drag_delta;
839 where = i->view->region()->position();
842 RegionView* new_view = insert_region_into_playlist (
843 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
850 new_views.push_back (new_view);
852 /* we don't need the copied RegionView any more */
853 views_to_delete.push_back (i->view);
856 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
857 because when views are deleted they are automagically removed from _views, which messes
860 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
864 /* If we've created new regions either by copying or moving
865 to a new track, we want to replace the old selection with the new ones
868 if (new_views.size() > 0) {
869 _editor->selection->set (new_views);
872 /* write commands for the accumulated diffs for all our modified playlists */
873 add_stateful_diff_commands_for_playlists (modified_playlists);
875 _editor->commit_reversible_command ();
879 RegionMoveDrag::finished_no_copy (
880 bool const changed_position,
881 bool const changed_tracks,
882 framecnt_t const drag_delta
885 RegionSelection new_views;
886 PlaylistSet modified_playlists;
887 PlaylistSet frozen_playlists;
890 /* all changes were made during motion event handlers */
891 _editor->commit_reversible_command ();
895 if (_x_constrained) {
896 _editor->begin_reversible_command (_("fixed time region drag"));
898 _editor->begin_reversible_command (_("region drag"));
901 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
903 RegionView* rv = i->view;
905 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
906 layer_t const dest_layer = i->layer;
908 if (rv->region()->locked()) {
915 if (changed_position && !_x_constrained) {
916 where = rv->region()->position() - drag_delta;
918 where = rv->region()->position();
921 if (changed_tracks) {
923 /* insert into new playlist */
925 RegionView* new_view = insert_region_into_playlist (
926 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
934 new_views.push_back (new_view);
936 /* remove from old playlist */
938 /* the region that used to be in the old playlist is not
939 moved to the new one - we use a copy of it. as a result,
940 any existing editor for the region should no longer be
943 rv->hide_region_editor();
944 rv->fake_set_opaque (false);
946 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
950 rv->region()->clear_changes ();
953 motion on the same track. plonk the previously reparented region
954 back to its original canvas group (its streamview).
955 No need to do anything for copies as they are fake regions which will be deleted.
958 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
959 rv->get_canvas_group()->property_y() = i->initial_y;
960 rv->get_time_axis_view().reveal_dependent_views (*rv);
962 /* just change the model */
964 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
966 if (dest_rtv->view()->layer_display() == Stacked) {
967 rv->region()->set_layer (dest_layer);
968 rv->region()->set_pending_explicit_relayer (true);
971 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
973 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
979 /* this movement may result in a crossfade being modified, so we need to get undo
980 data from the playlist as well as the region.
983 r = modified_playlists.insert (playlist);
985 playlist->clear_changes ();
988 rv->region()->set_position (where, (void*) this);
990 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
993 if (changed_tracks) {
995 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
996 was selected in all of them, then removing it from a playlist will have removed all
997 trace of it from _views (i.e. there were N regions selected, we removed 1,
998 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
999 corresponding regionview, and _views is now empty).
1001 This could have invalidated any and all iterators into _views.
1003 The heuristic we use here is: if the region selection is empty, break out of the loop
1004 here. if the region selection is not empty, then restart the loop because we know that
1005 we must have removed at least the region(view) we've just been working on as well as any
1006 that we processed on previous iterations.
1008 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1009 we can just iterate.
1013 if (_views.empty()) {
1024 /* If we've created new regions either by copying or moving
1025 to a new track, we want to replace the old selection with the new ones
1028 if (new_views.size() > 0) {
1029 _editor->selection->set (new_views);
1032 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1036 /* write commands for the accumulated diffs for all our modified playlists */
1037 add_stateful_diff_commands_for_playlists (modified_playlists);
1039 _editor->commit_reversible_command ();
1042 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1043 * @param region Region to remove.
1044 * @param playlist playlist To remove from.
1045 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1046 * that clear_changes () is only called once per playlist.
1049 RegionMoveDrag::remove_region_from_playlist (
1050 boost::shared_ptr<Region> region,
1051 boost::shared_ptr<Playlist> playlist,
1052 PlaylistSet& modified_playlists
1055 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1058 playlist->clear_changes ();
1061 playlist->remove_region (region);
1065 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1066 * clearing the playlist's diff history first if necessary.
1067 * @param region Region to insert.
1068 * @param dest_rtv Destination RouteTimeAxisView.
1069 * @param dest_layer Destination layer.
1070 * @param where Destination position.
1071 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1072 * that clear_changes () is only called once per playlist.
1073 * @return New RegionView, or 0 if no insert was performed.
1076 RegionMoveDrag::insert_region_into_playlist (
1077 boost::shared_ptr<Region> region,
1078 RouteTimeAxisView* dest_rtv,
1081 PlaylistSet& modified_playlists
1084 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1085 if (!dest_playlist) {
1089 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1090 _new_region_view = 0;
1091 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1093 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1094 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1096 dest_playlist->clear_changes ();
1099 dest_playlist->add_region (region, where);
1101 if (dest_rtv->view()->layer_display() == Stacked) {
1102 region->set_layer (dest_layer);
1103 region->set_pending_explicit_relayer (true);
1108 assert (_new_region_view);
1110 return _new_region_view;
1114 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1116 _new_region_view = rv;
1120 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1122 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1123 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1125 _editor->session()->add_command (new StatefulDiffCommand (*i));
1134 RegionMoveDrag::aborted ()
1138 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1145 RegionMotionDrag::aborted ();
1150 RegionMotionDrag::aborted ()
1152 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1153 RegionView* rv = i->view;
1154 TimeAxisView* tv = &(rv->get_time_axis_view ());
1155 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1157 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1158 rv->get_canvas_group()->property_y() = 0;
1159 rv->get_time_axis_view().reveal_dependent_views (*rv);
1160 rv->fake_set_opaque (false);
1161 rv->move (-_total_x_delta, 0);
1162 rv->set_height (rtv->view()->child_height ());
1165 _editor->update_canvas_now ();
1168 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1169 : RegionMotionDrag (e, i, p, v, b),
1172 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1175 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1176 if (rtv && rtv->is_track()) {
1177 speed = rtv->track()->speed ();
1180 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1184 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1186 RegionMotionDrag::start_grab (event, c);
1188 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1191 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1192 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1194 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1196 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1197 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1199 _primary = v->view()->create_region_view (r, false, false);
1201 _primary->get_canvas_group()->show ();
1202 _primary->set_position (pos, 0);
1203 _views.push_back (DraggingView (_primary, this));
1205 _last_frame_position = pos;
1207 _item = _primary->get_canvas_group ();
1211 RegionInsertDrag::finished (GdkEvent *, bool)
1213 _editor->update_canvas_now ();
1215 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1217 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1218 _primary->get_canvas_group()->property_y() = 0;
1220 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1222 _editor->begin_reversible_command (_("insert region"));
1223 playlist->clear_changes ();
1224 playlist->add_region (_primary->region (), _last_frame_position);
1225 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1226 _editor->commit_reversible_command ();
1234 RegionInsertDrag::aborted ()
1241 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1242 : RegionMoveDrag (e, i, p, v, false, false)
1244 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1247 struct RegionSelectionByPosition {
1248 bool operator() (RegionView*a, RegionView* b) {
1249 return a->region()->position () < b->region()->position();
1254 RegionSpliceDrag::motion (GdkEvent* event, bool)
1256 /* Which trackview is this ? */
1258 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1259 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1260 layer_t layer = tvp.second;
1262 if (tv && tv->layer_display() == Overlaid) {
1266 /* The region motion is only processed if the pointer is over
1270 if (!tv || !tv->is_track()) {
1271 /* To make sure we hide the verbose canvas cursor when the mouse is
1272 not held over and audiotrack.
1274 _editor->hide_verbose_canvas_cursor ();
1280 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1286 RegionSelection copy (_editor->selection->regions);
1288 RegionSelectionByPosition cmp;
1291 framepos_t const pf = adjusted_current_frame (event);
1293 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1295 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1301 boost::shared_ptr<Playlist> playlist;
1303 if ((playlist = atv->playlist()) == 0) {
1307 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1312 if (pf < (*i)->region()->last_frame() + 1) {
1316 if (pf > (*i)->region()->first_frame()) {
1322 playlist->shuffle ((*i)->region(), dir);
1327 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1329 RegionMoveDrag::finished (event, movement_occurred);
1333 RegionSpliceDrag::aborted ()
1338 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1340 _view (dynamic_cast<MidiTimeAxisView*> (v))
1342 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1348 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1354 framepos_t const f = adjusted_current_frame (event);
1355 if (f < grab_frame()) {
1356 _region->set_position (f, this);
1359 /* again, don't use a zero-length region (see above) */
1360 framecnt_t const len = abs (f - grab_frame ());
1361 _region->set_length (len < 1 ? 1 : len, this);
1367 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1369 if (!movement_occurred) {
1374 _editor->commit_reversible_command ();
1379 RegionCreateDrag::add_region ()
1381 if (_editor->session()) {
1382 const TempoMap& map (_editor->session()->tempo_map());
1383 framecnt_t pos = grab_frame();
1384 const Meter& m = map.meter_at (pos);
1385 /* not that the frame rate used here can be affected by pull up/down which
1388 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1389 _region = _view->add_region (grab_frame(), len, false);
1394 RegionCreateDrag::aborted ()
1399 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1403 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1407 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1409 Gdk::Cursor* cursor;
1410 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1411 float x_fraction = cnote->mouse_x_fraction ();
1413 if (x_fraction > 0.0 && x_fraction < 0.25) {
1414 cursor = _editor->cursors()->left_side_trim;
1416 cursor = _editor->cursors()->right_side_trim;
1419 Drag::start_grab (event, cursor);
1421 region = &cnote->region_view();
1423 double const region_start = region->get_position_pixels();
1424 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1426 if (grab_x() <= middle_point) {
1427 cursor = _editor->cursors()->left_side_trim;
1430 cursor = _editor->cursors()->right_side_trim;
1434 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1436 if (event->motion.state & Keyboard::PrimaryModifier) {
1442 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1444 if (ms.size() > 1) {
1445 /* has to be relative, may make no sense otherwise */
1449 /* select this note; if it is already selected, preserve the existing selection,
1450 otherwise make this note the only one selected.
1452 region->note_selected (cnote, cnote->selected ());
1454 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1455 MidiRegionSelection::iterator next;
1458 (*r)->begin_resizing (at_front);
1464 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1466 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1467 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1468 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1473 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1475 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1476 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1477 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1482 NoteResizeDrag::aborted ()
1487 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1490 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1494 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1500 RegionGainDrag::finished (GdkEvent *, bool)
1506 RegionGainDrag::aborted ()
1511 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1512 : RegionDrag (e, i, p, v)
1513 , _have_transaction (false)
1515 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1519 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1522 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1523 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1525 if (tv && tv->is_track()) {
1526 speed = tv->track()->speed();
1529 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1530 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1531 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1533 framepos_t const pf = adjusted_current_frame (event);
1535 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1536 /* Move the contents of the region around without changing the region bounds */
1537 _operation = ContentsTrim;
1538 Drag::start_grab (event, _editor->cursors()->trimmer);
1540 /* These will get overridden for a point trim.*/
1541 if (pf < (region_start + region_length/2)) {
1542 /* closer to front */
1543 _operation = StartTrim;
1544 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1547 _operation = EndTrim;
1548 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1552 switch (_operation) {
1554 _editor->show_verbose_time_cursor (region_start, 10);
1555 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1556 i->view->trim_front_starting ();
1560 _editor->show_verbose_time_cursor (region_end, 10);
1563 _editor->show_verbose_time_cursor (pf, 10);
1567 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1568 i->view->region()->suspend_property_changes ();
1573 TrimDrag::motion (GdkEvent* event, bool first_move)
1575 RegionView* rv = _primary;
1578 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1579 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1580 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1582 if (tv && tv->is_track()) {
1583 speed = tv->track()->speed();
1586 framecnt_t const dt = adjusted_current_frame (event) - grab_frame ();
1592 switch (_operation) {
1594 trim_type = "Region start trim";
1597 trim_type = "Region end trim";
1600 trim_type = "Region content trim";
1604 _editor->begin_reversible_command (trim_type);
1605 _have_transaction = true;
1607 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1608 RegionView* rv = i->view;
1609 rv->fake_set_opaque (false);
1610 rv->enable_display (false);
1611 rv->region()->clear_changes ();
1613 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1616 arv->temporarily_hide_envelope ();
1619 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1620 insert_result = _editor->motion_frozen_playlists.insert (pl);
1622 if (insert_result.second) {
1628 bool non_overlap_trim = false;
1630 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1631 non_overlap_trim = true;
1634 switch (_operation) {
1636 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1637 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1642 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1643 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1649 bool swap_direction = false;
1651 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1652 swap_direction = true;
1655 framecnt_t frame_delta = 0;
1657 bool left_direction = false;
1658 if (last_pointer_frame() > adjusted_current_frame(event)) {
1659 left_direction = true;
1662 if (left_direction) {
1663 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1665 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1668 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1669 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1675 switch (_operation) {
1677 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->position() / speed), 10);
1680 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->last_frame() / speed), 10);
1683 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1690 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1692 if (movement_occurred) {
1693 motion (event, false);
1695 /* This must happen before the region's StatefulDiffCommand is created, as it may
1696 `correct' (ahem) the region's _start from being negative to being zero. It
1697 needs to be zero in the undo record.
1699 if (_operation == StartTrim) {
1700 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1701 i->view->trim_front_ending ();
1705 if (!_editor->selection->selected (_primary)) {
1706 _primary->thaw_after_trim ();
1709 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1710 i->view->thaw_after_trim ();
1711 i->view->enable_display (true);
1712 i->view->fake_set_opaque (true);
1713 if (_have_transaction) {
1714 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1718 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1722 _editor->motion_frozen_playlists.clear ();
1723 if (_have_transaction) {
1724 _editor->commit_reversible_command();
1728 /* no mouse movement */
1729 _editor->point_trim (event, adjusted_current_frame (event));
1732 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1733 if (_operation == StartTrim) {
1734 i->view->trim_front_ending ();
1737 i->view->region()->resume_property_changes ();
1742 TrimDrag::aborted ()
1744 /* Our motion method is changing model state, so use the Undo system
1745 to cancel. Perhaps not ideal, as this will leave an Undo point
1746 behind which may be slightly odd from the user's point of view.
1751 if (_have_transaction) {
1755 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1756 i->view->region()->resume_property_changes ();
1760 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1764 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1766 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1771 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1774 // create a dummy marker for visual representation of moving the copy.
1775 // The actual copying is not done before we reach the finish callback.
1777 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1779 MeterMarker* new_marker = new MeterMarker (
1781 *_editor->meter_group,
1782 *_editor->cursor_group,
1783 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1785 *new MeterSection (_marker->meter())
1788 _item = &new_marker->the_item ();
1789 _marker = new_marker;
1793 MetricSection& section (_marker->meter());
1795 if (!section.movable()) {
1801 Drag::start_grab (event, cursor);
1803 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1805 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1809 MeterMarkerDrag::motion (GdkEvent* event, bool)
1811 framepos_t const pf = adjusted_current_frame (event);
1813 _marker->set_position (pf);
1815 _editor->show_verbose_time_cursor (pf, 10);
1819 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1821 if (!movement_occurred) {
1825 motion (event, false);
1827 Timecode::BBT_Time when;
1829 TempoMap& map (_editor->session()->tempo_map());
1830 map.bbt_time (last_pointer_frame(), when);
1832 if (_copy == true) {
1833 _editor->begin_reversible_command (_("copy meter mark"));
1834 XMLNode &before = map.get_state();
1835 map.add_meter (_marker->meter(), when);
1836 XMLNode &after = map.get_state();
1837 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1838 _editor->commit_reversible_command ();
1840 // delete the dummy marker we used for visual representation of copying.
1841 // a new visual marker will show up automatically.
1844 _editor->begin_reversible_command (_("move meter mark"));
1845 XMLNode &before = map.get_state();
1846 map.move_meter (_marker->meter(), when);
1847 XMLNode &after = map.get_state();
1848 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1849 _editor->commit_reversible_command ();
1854 MeterMarkerDrag::aborted ()
1856 _marker->set_position (_marker->meter().frame ());
1859 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1863 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1865 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1870 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1874 // create a dummy marker for visual representation of moving the copy.
1875 // The actual copying is not done before we reach the finish callback.
1877 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1879 TempoMarker* new_marker = new TempoMarker (
1881 *_editor->tempo_group,
1882 *_editor->cursor_group,
1883 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1885 *new TempoSection (_marker->tempo())
1888 _item = &new_marker->the_item ();
1889 _marker = new_marker;
1893 Drag::start_grab (event, cursor);
1895 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1896 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1900 TempoMarkerDrag::motion (GdkEvent* event, bool)
1902 framepos_t const pf = adjusted_current_frame (event);
1903 _marker->set_position (pf);
1904 _editor->show_verbose_time_cursor (pf, 10);
1908 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1910 if (!movement_occurred) {
1914 motion (event, false);
1916 Timecode::BBT_Time when;
1918 TempoMap& map (_editor->session()->tempo_map());
1919 map.bbt_time (last_pointer_frame(), when);
1921 if (_copy == true) {
1922 _editor->begin_reversible_command (_("copy tempo mark"));
1923 XMLNode &before = map.get_state();
1924 map.add_tempo (_marker->tempo(), when);
1925 XMLNode &after = map.get_state();
1926 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1927 _editor->commit_reversible_command ();
1929 // delete the dummy marker we used for visual representation of copying.
1930 // a new visual marker will show up automatically.
1933 _editor->begin_reversible_command (_("move tempo mark"));
1934 XMLNode &before = map.get_state();
1935 map.move_tempo (_marker->tempo(), when);
1936 XMLNode &after = map.get_state();
1937 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1938 _editor->commit_reversible_command ();
1943 TempoMarkerDrag::aborted ()
1945 _marker->set_position (_marker->tempo().frame());
1948 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1952 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
1954 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1959 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1961 Drag::start_grab (event, c);
1965 framepos_t where = _editor->event_frame (event, 0, 0);
1967 _editor->snap_to_with_modifier (where, event);
1968 _editor->playhead_cursor->set_position (where);
1972 if (_cursor == _editor->playhead_cursor) {
1973 _editor->_dragging_playhead = true;
1975 Session* s = _editor->session ();
1978 if (_was_rolling && _stop) {
1982 if (s->is_auditioning()) {
1983 s->cancel_audition ();
1986 s->request_suspend_timecode_transmission ();
1988 if (s->timecode_transmission_suspended ()) {
1989 framepos_t const f = _editor->playhead_cursor->current_frame;
1990 s->send_mmc_locate (f);
1991 s->send_full_time_code (f);
1996 _pointer_frame_offset = raw_grab_frame() - _cursor->current_frame;
1998 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2002 CursorDrag::motion (GdkEvent* event, bool)
2004 framepos_t const adjusted_frame = adjusted_current_frame (event);
2006 if (adjusted_frame == last_pointer_frame()) {
2010 _cursor->set_position (adjusted_frame);
2012 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2014 Session* s = _editor->session ();
2015 if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
2016 framepos_t const f = _editor->playhead_cursor->current_frame;
2017 s->send_mmc_locate (f);
2018 s->send_full_time_code (f);
2023 _editor->update_canvas_now ();
2025 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2029 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2031 _editor->_dragging_playhead = false;
2033 if (!movement_occurred && _stop) {
2037 motion (event, false);
2039 if (_item == &_editor->playhead_cursor->canvas_item) {
2040 Session* s = _editor->session ();
2042 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2043 _editor->_pending_locate_request = true;
2044 s->request_resume_timecode_transmission ();
2050 CursorDrag::aborted ()
2052 if (_editor->_dragging_playhead) {
2053 _editor->session()->request_resume_timecode_transmission ();
2054 _editor->_dragging_playhead = false;
2057 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2060 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2061 : RegionDrag (e, i, p, v)
2063 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2067 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2069 Drag::start_grab (event, cursor);
2071 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2072 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2074 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2075 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2077 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2081 FadeInDrag::motion (GdkEvent* event, bool)
2083 framecnt_t fade_length;
2085 framepos_t const pos = adjusted_current_frame (event);
2087 boost::shared_ptr<Region> region = _primary->region ();
2089 if (pos < (region->position() + 64)) {
2090 fade_length = 64; // this should be a minimum defined somewhere
2091 } else if (pos > region->last_frame()) {
2092 fade_length = region->length();
2094 fade_length = pos - region->position();
2097 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2099 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2105 tmp->reset_fade_in_shape_width (fade_length);
2106 tmp->show_fade_line((framecnt_t) fade_length);
2109 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2113 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2115 if (!movement_occurred) {
2119 framecnt_t fade_length;
2121 framepos_t const pos = adjusted_current_frame (event);
2123 boost::shared_ptr<Region> region = _primary->region ();
2125 if (pos < (region->position() + 64)) {
2126 fade_length = 64; // this should be a minimum defined somewhere
2127 } else if (pos > region->last_frame()) {
2128 fade_length = region->length();
2130 fade_length = pos - region->position();
2133 _editor->begin_reversible_command (_("change fade in length"));
2135 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2137 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2143 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2144 XMLNode &before = alist->get_state();
2146 tmp->audio_region()->set_fade_in_length (fade_length);
2147 tmp->audio_region()->set_fade_in_active (true);
2148 tmp->hide_fade_line();
2150 XMLNode &after = alist->get_state();
2151 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2154 _editor->commit_reversible_command ();
2158 FadeInDrag::aborted ()
2160 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2161 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2167 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2168 tmp->hide_fade_line();
2172 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2173 : RegionDrag (e, i, p, v)
2175 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2179 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2181 Drag::start_grab (event, cursor);
2183 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2184 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2186 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2187 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2189 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2193 FadeOutDrag::motion (GdkEvent* event, bool)
2195 framecnt_t fade_length;
2197 framepos_t const pos = adjusted_current_frame (event);
2199 boost::shared_ptr<Region> region = _primary->region ();
2201 if (pos > (region->last_frame() - 64)) {
2202 fade_length = 64; // this should really be a minimum fade defined somewhere
2204 else if (pos < region->position()) {
2205 fade_length = region->length();
2208 fade_length = region->last_frame() - pos;
2211 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2213 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2219 tmp->reset_fade_out_shape_width (fade_length);
2220 tmp->show_fade_line(region->length() - fade_length);
2223 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2227 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2229 if (!movement_occurred) {
2233 framecnt_t fade_length;
2235 framepos_t const pos = adjusted_current_frame (event);
2237 boost::shared_ptr<Region> region = _primary->region ();
2239 if (pos > (region->last_frame() - 64)) {
2240 fade_length = 64; // this should really be a minimum fade defined somewhere
2242 else if (pos < region->position()) {
2243 fade_length = region->length();
2246 fade_length = region->last_frame() - pos;
2249 _editor->begin_reversible_command (_("change fade out length"));
2251 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2253 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2259 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2260 XMLNode &before = alist->get_state();
2262 tmp->audio_region()->set_fade_out_length (fade_length);
2263 tmp->audio_region()->set_fade_out_active (true);
2264 tmp->hide_fade_line();
2266 XMLNode &after = alist->get_state();
2267 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2270 _editor->commit_reversible_command ();
2274 FadeOutDrag::aborted ()
2276 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2277 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2283 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2284 tmp->hide_fade_line();
2288 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2291 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2293 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2296 _points.push_back (Gnome::Art::Point (0, 0));
2297 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2299 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2300 _line->property_width_pixels() = 1;
2301 _line->property_points () = _points;
2304 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2307 MarkerDrag::~MarkerDrag ()
2309 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2315 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2317 Drag::start_grab (event, cursor);
2321 Location *location = _editor->find_location_from_marker (_marker, is_start);
2322 _editor->_dragging_edit_point = true;
2324 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2326 update_item (location);
2328 // _drag_line->show();
2329 // _line->raise_to_top();
2332 _editor->show_verbose_time_cursor (location->start(), 10);
2334 _editor->show_verbose_time_cursor (location->end(), 10);
2337 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2340 case Selection::Toggle:
2341 _editor->selection->toggle (_marker);
2343 case Selection::Set:
2344 if (!_editor->selection->selected (_marker)) {
2345 _editor->selection->set (_marker);
2348 case Selection::Extend:
2350 Locations::LocationList ll;
2351 list<Marker*> to_add;
2353 _editor->selection->markers.range (s, e);
2354 s = min (_marker->position(), s);
2355 e = max (_marker->position(), e);
2358 if (e < max_framepos) {
2361 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2362 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2363 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2366 to_add.push_back (lm->start);
2369 to_add.push_back (lm->end);
2373 if (!to_add.empty()) {
2374 _editor->selection->add (to_add);
2378 case Selection::Add:
2379 _editor->selection->add (_marker);
2383 /* Set up copies for us to manipulate during the drag */
2385 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2386 Location* l = _editor->find_location_from_marker (*i, is_start);
2387 _copied_locations.push_back (new Location (*l));
2392 MarkerDrag::motion (GdkEvent* event, bool)
2394 framecnt_t f_delta = 0;
2396 bool move_both = false;
2398 Location *real_location;
2399 Location *copy_location = 0;
2401 framepos_t const newframe = adjusted_current_frame (event);
2403 framepos_t next = newframe;
2405 if (newframe == last_pointer_frame()) {
2409 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2413 MarkerSelection::iterator i;
2414 list<Location*>::iterator x;
2416 /* find the marker we're dragging, and compute the delta */
2418 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2419 x != _copied_locations.end() && i != _editor->selection->markers.end();
2425 if (marker == _marker) {
2427 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2432 if (real_location->is_mark()) {
2433 f_delta = newframe - copy_location->start();
2437 switch (marker->type()) {
2438 case Marker::SessionStart:
2439 case Marker::RangeStart:
2440 case Marker::LoopStart:
2441 case Marker::PunchIn:
2442 f_delta = newframe - copy_location->start();
2445 case Marker::SessionEnd:
2446 case Marker::RangeEnd:
2447 case Marker::LoopEnd:
2448 case Marker::PunchOut:
2449 f_delta = newframe - copy_location->end();
2452 /* what kind of marker is this ? */
2460 if (i == _editor->selection->markers.end()) {
2461 /* hmm, impossible - we didn't find the dragged marker */
2465 /* now move them all */
2467 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2468 x != _copied_locations.end() && i != _editor->selection->markers.end();
2474 /* call this to find out if its the start or end */
2476 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2480 if (real_location->locked()) {
2484 if (copy_location->is_mark()) {
2488 copy_location->set_start (copy_location->start() + f_delta);
2492 framepos_t new_start = copy_location->start() + f_delta;
2493 framepos_t new_end = copy_location->end() + f_delta;
2495 if (is_start) { // start-of-range marker
2498 copy_location->set_start (new_start);
2499 copy_location->set_end (new_end);
2500 } else if (new_start < copy_location->end()) {
2501 copy_location->set_start (new_start);
2503 _editor->snap_to (next, 1, true);
2504 copy_location->set_end (next);
2505 copy_location->set_start (newframe);
2508 } else { // end marker
2511 copy_location->set_end (new_end);
2512 copy_location->set_start (new_start);
2513 } else if (new_end > copy_location->start()) {
2514 copy_location->set_end (new_end);
2515 } else if (newframe > 0) {
2516 _editor->snap_to (next, -1, true);
2517 copy_location->set_start (next);
2518 copy_location->set_end (newframe);
2523 update_item (copy_location);
2525 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2528 lm->set_position (copy_location->start(), copy_location->end());
2532 assert (!_copied_locations.empty());
2534 _editor->show_verbose_time_cursor (newframe, 10);
2537 _editor->update_canvas_now ();
2542 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2544 if (!movement_occurred) {
2546 /* just a click, do nothing but finish
2547 off the selection process
2550 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2553 case Selection::Set:
2554 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2555 _editor->selection->set (_marker);
2559 case Selection::Toggle:
2560 case Selection::Extend:
2561 case Selection::Add:
2568 _editor->_dragging_edit_point = false;
2570 _editor->begin_reversible_command ( _("move marker") );
2571 XMLNode &before = _editor->session()->locations()->get_state();
2573 MarkerSelection::iterator i;
2574 list<Location*>::iterator x;
2577 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2578 x != _copied_locations.end() && i != _editor->selection->markers.end();
2581 Location * location = _editor->find_location_from_marker (*i, is_start);
2585 if (location->locked()) {
2589 if (location->is_mark()) {
2590 location->set_start ((*x)->start());
2592 location->set ((*x)->start(), (*x)->end());
2597 XMLNode &after = _editor->session()->locations()->get_state();
2598 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2599 _editor->commit_reversible_command ();
2605 MarkerDrag::aborted ()
2611 MarkerDrag::update_item (Location* location)
2613 double const x1 = _editor->frame_to_pixel (location->start());
2615 _points.front().set_x(x1);
2616 _points.back().set_x(x1);
2617 _line->property_points() = _points;
2620 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2622 _cumulative_x_drag (0),
2623 _cumulative_y_drag (0)
2625 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2627 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2633 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2635 Drag::start_grab (event, _editor->cursors()->fader);
2637 // start the grab at the center of the control point so
2638 // the point doesn't 'jump' to the mouse after the first drag
2639 _fixed_grab_x = _point->get_x();
2640 _fixed_grab_y = _point->get_y();
2642 float const fraction = 1 - (_point->get_y() / _point->line().height());
2644 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2646 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2647 event->button.x + 10, event->button.y + 10);
2649 _editor->show_verbose_canvas_cursor ();
2653 ControlPointDrag::motion (GdkEvent* event, bool)
2655 double dx = _drags->current_pointer_x() - last_pointer_x();
2656 double dy = _drags->current_pointer_y() - last_pointer_y();
2658 if (event->button.state & Keyboard::SecondaryModifier) {
2663 /* coordinate in pixels relative to the start of the region (for region-based automation)
2664 or track (for track-based automation) */
2665 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2666 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2668 // calculate zero crossing point. back off by .01 to stay on the
2669 // positive side of zero
2670 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2672 // make sure we hit zero when passing through
2673 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2677 if (_x_constrained) {
2680 if (_y_constrained) {
2684 _cumulative_x_drag = cx - _fixed_grab_x;
2685 _cumulative_y_drag = cy - _fixed_grab_y;
2689 cy = min ((double) _point->line().height(), cy);
2691 framepos_t cx_frames = _editor->unit_to_frame (cx);
2693 if (!_x_constrained) {
2694 _editor->snap_to_with_modifier (cx_frames, event);
2697 cx_frames = min (cx_frames, _point->line().maximum_time());
2699 float const fraction = 1.0 - (cy / _point->line().height());
2701 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2703 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2705 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2709 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2711 if (!movement_occurred) {
2715 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2716 _editor->reset_point_selection ();
2720 motion (event, false);
2723 _point->line().end_drag ();
2724 _editor->session()->commit_reversible_command ();
2728 ControlPointDrag::aborted ()
2730 _point->line().reset ();
2734 ControlPointDrag::active (Editing::MouseMode m)
2736 if (m == Editing::MouseGain) {
2737 /* always active in mouse gain */
2741 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2742 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2745 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2748 _cumulative_y_drag (0)
2750 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2754 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2756 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2759 _item = &_line->grab_item ();
2761 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2762 origin, and ditto for y.
2765 double cx = event->button.x;
2766 double cy = event->button.y;
2768 _line->parent_group().w2i (cx, cy);
2770 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2775 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2776 /* no adjacent points */
2780 Drag::start_grab (event, _editor->cursors()->fader);
2782 /* store grab start in parent frame */
2787 double fraction = 1.0 - (cy / _line->height());
2789 _line->start_drag_line (before, after, fraction);
2791 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2792 event->button.x + 10, event->button.y + 10);
2794 _editor->show_verbose_canvas_cursor ();
2798 LineDrag::motion (GdkEvent* event, bool)
2800 double dy = _drags->current_pointer_y() - last_pointer_y();
2802 if (event->button.state & Keyboard::SecondaryModifier) {
2806 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2808 _cumulative_y_drag = cy - _fixed_grab_y;
2811 cy = min ((double) _line->height(), cy);
2813 double const fraction = 1.0 - (cy / _line->height());
2817 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2823 /* we are ignoring x position for this drag, so we can just pass in anything */
2824 _line->drag_motion (0, fraction, true, push);
2826 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2830 LineDrag::finished (GdkEvent* event, bool)
2832 motion (event, false);
2834 _editor->session()->commit_reversible_command ();
2838 LineDrag::aborted ()
2843 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2846 _cumulative_x_drag (0)
2848 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2852 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2854 Drag::start_grab (event);
2856 _line = reinterpret_cast<SimpleLine*> (_item);
2859 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2861 double cx = event->button.x;
2862 double cy = event->button.y;
2864 _item->property_parent().get_value()->w2i(cx, cy);
2866 /* store grab start in parent frame */
2867 _region_view_grab_x = cx;
2869 _before = _line->property_x1();
2871 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2873 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2877 FeatureLineDrag::motion (GdkEvent*, bool)
2879 double dx = _drags->current_pointer_x() - last_pointer_x();
2881 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2883 _cumulative_x_drag += dx;
2885 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2894 _line->property_x1() = cx;
2895 _line->property_x2() = cx;
2897 _before = _line->property_x1();
2901 FeatureLineDrag::finished (GdkEvent*, bool)
2903 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2904 _arv->update_transient(_before, _line->property_x1());
2908 FeatureLineDrag::aborted ()
2913 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2916 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2920 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2922 Drag::start_grab (event);
2923 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2927 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2934 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2936 framepos_t grab = grab_frame ();
2937 if (Config->get_rubberbanding_snaps_to_grid ()) {
2938 _editor->snap_to_with_modifier (grab, event);
2941 /* base start and end on initial click position */
2951 if (_drags->current_pointer_y() < grab_y()) {
2952 y1 = _drags->current_pointer_y();
2955 y2 = _drags->current_pointer_y();
2960 if (start != end || y1 != y2) {
2962 double x1 = _editor->frame_to_pixel (start);
2963 double x2 = _editor->frame_to_pixel (end);
2965 _editor->rubberband_rect->property_x1() = x1;
2966 _editor->rubberband_rect->property_y1() = y1;
2967 _editor->rubberband_rect->property_x2() = x2;
2968 _editor->rubberband_rect->property_y2() = y2;
2970 _editor->rubberband_rect->show();
2971 _editor->rubberband_rect->raise_to_top();
2973 _editor->show_verbose_time_cursor (pf, 10);
2978 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2980 if (movement_occurred) {
2982 motion (event, false);
2985 if (_drags->current_pointer_y() < grab_y()) {
2986 y1 = _drags->current_pointer_y();
2989 y2 = _drags->current_pointer_y();
2994 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2997 _editor->begin_reversible_command (_("rubberband selection"));
2999 if (grab_frame() < last_pointer_frame()) {
3000 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3002 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3006 _editor->commit_reversible_command ();
3010 if (!getenv("ARDOUR_SAE")) {
3011 _editor->selection->clear_tracks();
3013 _editor->selection->clear_regions();
3014 _editor->selection->clear_points ();
3015 _editor->selection->clear_lines ();
3018 _editor->rubberband_rect->hide();
3022 RubberbandSelectDrag::aborted ()
3024 _editor->rubberband_rect->hide ();
3027 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3028 : RegionDrag (e, i, p, v)
3030 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3034 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3036 Drag::start_grab (event, cursor);
3038 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3042 TimeFXDrag::motion (GdkEvent* event, bool)
3044 RegionView* rv = _primary;
3046 framepos_t const pf = adjusted_current_frame (event);
3048 if (pf > rv->region()->position()) {
3049 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3052 _editor->show_verbose_time_cursor (pf, 10);
3056 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3058 _primary->get_time_axis_view().hide_timestretch ();
3060 if (!movement_occurred) {
3064 if (last_pointer_frame() < _primary->region()->position()) {
3065 /* backwards drag of the left edge - not usable */
3069 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3071 float percentage = (double) newlen / (double) _primary->region()->length();
3073 #ifndef USE_RUBBERBAND
3074 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3075 if (_primary->region()->data_type() == DataType::AUDIO) {
3076 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3080 _editor->begin_reversible_command (_("timestretch"));
3082 // XXX how do timeFX on multiple regions ?
3087 if (_editor->time_stretch (rs, percentage) == -1) {
3088 error << _("An error occurred while executing time stretch operation") << endmsg;
3093 TimeFXDrag::aborted ()
3095 _primary->get_time_axis_view().hide_timestretch ();
3098 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3101 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3105 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3107 Drag::start_grab (event);
3111 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3113 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3117 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3119 if (movement_occurred && _editor->session()) {
3120 /* make sure we stop */
3121 _editor->session()->request_transport_speed (0.0);
3126 ScrubDrag::aborted ()
3131 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3135 , _original_pointer_time_axis (-1)
3136 , _last_pointer_time_axis (-1)
3138 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3142 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3144 framepos_t start = 0;
3147 if (_editor->session() == 0) {
3151 Gdk::Cursor* cursor = 0;
3153 switch (_operation) {
3154 case CreateSelection:
3155 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3160 cursor = _editor->cursors()->selector;
3161 Drag::start_grab (event, cursor);
3164 case SelectionStartTrim:
3165 if (_editor->clicked_axisview) {
3166 _editor->clicked_axisview->order_selection_trims (_item, true);
3168 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3169 start = _editor->selection->time[_editor->clicked_selection].start;
3170 _pointer_frame_offset = raw_grab_frame() - start;
3173 case SelectionEndTrim:
3174 if (_editor->clicked_axisview) {
3175 _editor->clicked_axisview->order_selection_trims (_item, false);
3177 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3178 end = _editor->selection->time[_editor->clicked_selection].end;
3179 _pointer_frame_offset = raw_grab_frame() - end;
3183 start = _editor->selection->time[_editor->clicked_selection].start;
3184 Drag::start_grab (event, cursor);
3185 _pointer_frame_offset = raw_grab_frame() - start;
3189 if (_operation == SelectionMove) {
3190 _editor->show_verbose_time_cursor (start, 10);
3192 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3195 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3199 SelectionDrag::motion (GdkEvent* event, bool first_move)
3201 framepos_t start = 0;
3205 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3206 if (pending_time_axis.first == 0) {
3210 framepos_t const pending_position = adjusted_current_frame (event);
3212 /* only alter selection if things have changed */
3214 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3218 switch (_operation) {
3219 case CreateSelection:
3221 framepos_t grab = grab_frame ();
3224 _editor->snap_to (grab);
3227 if (pending_position < grab_frame()) {
3228 start = pending_position;
3231 end = pending_position;
3235 /* first drag: Either add to the selection
3236 or create a new selection
3242 /* adding to the selection */
3243 _editor->set_selected_track_as_side_effect (Selection::Add);
3244 //_editor->selection->add (_editor->clicked_axisview);
3245 _editor->clicked_selection = _editor->selection->add (start, end);
3250 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3251 //_editor->selection->set (_editor->clicked_axisview);
3252 _editor->set_selected_track_as_side_effect (Selection::Set);
3255 _editor->clicked_selection = _editor->selection->set (start, end);
3259 /* select the track that we're in */
3260 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3261 // _editor->set_selected_track_as_side_effect (Selection::Add);
3262 _editor->selection->add (pending_time_axis.first);
3263 _added_time_axes.push_back (pending_time_axis.first);
3266 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3267 tracks that we selected in the first place.
3270 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3271 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3273 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3274 while (i != _added_time_axes.end()) {
3276 list<TimeAxisView*>::iterator tmp = i;
3279 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3280 _editor->selection->remove (*i);
3281 _added_time_axes.remove (*i);
3290 case SelectionStartTrim:
3292 start = _editor->selection->time[_editor->clicked_selection].start;
3293 end = _editor->selection->time[_editor->clicked_selection].end;
3295 if (pending_position > end) {
3298 start = pending_position;
3302 case SelectionEndTrim:
3304 start = _editor->selection->time[_editor->clicked_selection].start;
3305 end = _editor->selection->time[_editor->clicked_selection].end;
3307 if (pending_position < start) {
3310 end = pending_position;
3317 start = _editor->selection->time[_editor->clicked_selection].start;
3318 end = _editor->selection->time[_editor->clicked_selection].end;
3320 length = end - start;
3322 start = pending_position;
3323 _editor->snap_to (start);
3325 end = start + length;
3330 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3331 _editor->start_canvas_autoscroll (1, 0);
3335 _editor->selection->replace (_editor->clicked_selection, start, end);
3338 if (_operation == SelectionMove) {
3339 _editor->show_verbose_time_cursor(start, 10);
3341 _editor->show_verbose_time_cursor(pending_position, 10);
3346 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3348 Session* s = _editor->session();
3350 if (movement_occurred) {
3351 motion (event, false);
3352 /* XXX this is not object-oriented programming at all. ick */
3353 if (_editor->selection->time.consolidate()) {
3354 _editor->selection->TimeChanged ();
3357 /* XXX what if its a music time selection? */
3358 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3359 s->request_play_range (&_editor->selection->time, true);
3364 /* just a click, no pointer movement.*/
3366 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3367 _editor->selection->clear_time();
3370 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3371 _editor->selection->set (_editor->clicked_axisview);
3374 if (s && s->get_play_range () && s->transport_rolling()) {
3375 s->request_stop (false, false);
3380 _editor->stop_canvas_autoscroll ();
3384 SelectionDrag::aborted ()
3389 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3394 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3396 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3397 physical_screen_height (_editor->get_window()));
3398 _drag_rect->hide ();
3400 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3401 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3405 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3407 if (_editor->session() == 0) {
3411 Gdk::Cursor* cursor = 0;
3413 if (!_editor->temp_location) {
3414 _editor->temp_location = new Location (*_editor->session());
3417 switch (_operation) {
3418 case CreateRangeMarker:
3419 case CreateTransportMarker:
3420 case CreateCDMarker:
3422 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3427 cursor = _editor->cursors()->selector;
3431 Drag::start_grab (event, cursor);
3433 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3437 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3439 framepos_t start = 0;
3441 ArdourCanvas::SimpleRect *crect;
3443 switch (_operation) {
3444 case CreateRangeMarker:
3445 crect = _editor->range_bar_drag_rect;
3447 case CreateTransportMarker:
3448 crect = _editor->transport_bar_drag_rect;
3450 case CreateCDMarker:
3451 crect = _editor->cd_marker_bar_drag_rect;
3454 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3459 framepos_t const pf = adjusted_current_frame (event);
3461 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3462 framepos_t grab = grab_frame ();
3463 _editor->snap_to (grab);
3465 if (pf < grab_frame()) {
3473 /* first drag: Either add to the selection
3474 or create a new selection.
3479 _editor->temp_location->set (start, end);
3483 update_item (_editor->temp_location);
3485 //_drag_rect->raise_to_top();
3490 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3491 _editor->start_canvas_autoscroll (1, 0);
3495 _editor->temp_location->set (start, end);
3497 double x1 = _editor->frame_to_pixel (start);
3498 double x2 = _editor->frame_to_pixel (end);
3499 crect->property_x1() = x1;
3500 crect->property_x2() = x2;
3502 update_item (_editor->temp_location);
3505 _editor->show_verbose_time_cursor (pf, 10);
3510 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3512 Location * newloc = 0;
3516 if (movement_occurred) {
3517 motion (event, false);
3520 switch (_operation) {
3521 case CreateRangeMarker:
3522 case CreateCDMarker:
3524 _editor->begin_reversible_command (_("new range marker"));
3525 XMLNode &before = _editor->session()->locations()->get_state();
3526 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3527 if (_operation == CreateCDMarker) {
3528 flags = Location::IsRangeMarker | Location::IsCDMarker;
3529 _editor->cd_marker_bar_drag_rect->hide();
3532 flags = Location::IsRangeMarker;
3533 _editor->range_bar_drag_rect->hide();
3535 newloc = new Location (
3536 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3539 _editor->session()->locations()->add (newloc, true);
3540 XMLNode &after = _editor->session()->locations()->get_state();
3541 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3542 _editor->commit_reversible_command ();
3546 case CreateTransportMarker:
3547 // popup menu to pick loop or punch
3548 _editor->new_transport_marker_context_menu (&event->button, _item);
3552 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3554 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3559 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3561 if (end == max_framepos) {
3562 end = _editor->session()->current_end_frame ();
3565 if (start == max_framepos) {
3566 start = _editor->session()->current_start_frame ();
3569 switch (_editor->mouse_mode) {
3571 /* find the two markers on either side and then make the selection from it */
3572 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3576 /* find the two markers on either side of the click and make the range out of it */
3577 _editor->selection->set (start, end);
3586 _editor->stop_canvas_autoscroll ();
3590 RangeMarkerBarDrag::aborted ()
3596 RangeMarkerBarDrag::update_item (Location* location)
3598 double const x1 = _editor->frame_to_pixel (location->start());
3599 double const x2 = _editor->frame_to_pixel (location->end());
3601 _drag_rect->property_x1() = x1;
3602 _drag_rect->property_x2() = x2;
3605 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3609 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3613 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3615 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3616 Drag::start_grab (event, _editor->cursors()->zoom_out);
3619 Drag::start_grab (event, _editor->cursors()->zoom_in);
3623 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3627 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3632 framepos_t const pf = adjusted_current_frame (event);
3634 framepos_t grab = grab_frame ();
3635 _editor->snap_to_with_modifier (grab, event);
3637 /* base start and end on initial click position */
3649 _editor->zoom_rect->show();
3650 _editor->zoom_rect->raise_to_top();
3653 _editor->reposition_zoom_rect(start, end);
3655 _editor->show_verbose_time_cursor (pf, 10);
3660 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3662 if (movement_occurred) {
3663 motion (event, false);
3665 if (grab_frame() < last_pointer_frame()) {
3666 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3668 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3671 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3674 _editor->zoom_rect->hide();
3678 MouseZoomDrag::aborted ()
3680 _editor->zoom_rect->hide ();
3683 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3685 , _cumulative_dx (0)
3686 , _cumulative_dy (0)
3688 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3690 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3691 _region = &_primary->region_view ();
3692 _note_height = _region->midi_stream_view()->note_height ();
3696 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3698 Drag::start_grab (event);
3700 if (!(_was_selected = _primary->selected())) {
3702 /* tertiary-click means extend selection - we'll do that on button release,
3703 so don't add it here, because otherwise we make it hard to figure
3704 out the "extend-to" range.
3707 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3710 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3713 _region->note_selected (_primary, true);
3715 _region->unique_select (_primary);
3721 /** @return Current total drag x change in frames */
3723 NoteDrag::total_dx () const
3726 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3728 /* primary note time */
3729 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3731 /* new time of the primary note relative to the region position */
3732 frameoffset_t const st = n + dx;
3734 /* snap and return corresponding delta */
3735 return _region->snap_frame_to_frame (st) - n;
3738 /** @return Current total drag y change in notes */
3740 NoteDrag::total_dy () const
3742 /* this is `backwards' to make increasing note number go in the right direction */
3743 double const dy = _drags->current_pointer_y() - grab_y();
3748 if (abs (dy) >= _note_height) {
3750 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3752 ndy = (int8_t) floor (dy / _note_height / 2.0);
3756 /* more positive value = higher pitch and higher y-axis position on track,
3757 which is the inverse of the X-centric geometric universe
3764 NoteDrag::motion (GdkEvent *, bool)
3766 /* Total change in x and y since the start of the drag */
3767 frameoffset_t const dx = total_dx ();
3768 int8_t const dy = -total_dy ();
3770 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3771 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3772 double const tdy = dy * _note_height - _cumulative_dy;
3775 _cumulative_dx += tdx;
3776 _cumulative_dy += tdy;
3778 int8_t note_delta = total_dy();
3780 _region->move_selection (tdx, tdy, note_delta);
3783 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3784 (int) floor (_primary->note()->note() + note_delta));
3786 _editor->show_verbose_canvas_cursor_with (buf);
3791 NoteDrag::finished (GdkEvent* ev, bool moved)
3794 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3796 if (_was_selected) {
3797 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3799 _region->note_deselected (_primary);
3802 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3803 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3805 if (!extend && !add && _region->selection_size() > 1) {
3806 _region->unique_select (_primary);
3807 } else if (extend) {
3808 _region->note_selected (_primary, true, true);
3810 /* it was added during button press */
3815 _region->note_dropped (_primary, total_dx(), total_dy());
3820 NoteDrag::aborted ()
3825 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3826 : Drag (editor, item)
3828 , _nothing_to_drag (false)
3830 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3832 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3835 /* get all lines in the automation view */
3836 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3838 /* find those that overlap the ranges being dragged */
3839 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3840 while (i != lines.end ()) {
3841 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3844 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3846 /* check this range against all the AudioRanges that we are using */
3847 list<AudioRange>::const_iterator k = _ranges.begin ();
3848 while (k != _ranges.end()) {
3849 if (k->coverage (r.first, r.second) != OverlapNone) {
3855 /* add it to our list if it overlaps at all */
3856 if (k != _ranges.end()) {
3861 _lines.push_back (n);
3867 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3871 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3873 Drag::start_grab (event, cursor);
3875 /* Get line states before we start changing things */
3876 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3877 i->state = &i->line->get_state ();
3880 if (_ranges.empty()) {
3882 /* No selected time ranges: drag all points */
3883 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3884 uint32_t const N = i->line->npoints ();
3885 for (uint32_t j = 0; j < N; ++j) {
3886 i->points.push_back (i->line->nth (j));
3892 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3894 framecnt_t const half = (i->start + i->end) / 2;
3896 /* find the line that this audio range starts in */
3897 list<Line>::iterator j = _lines.begin();
3898 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3902 if (j != _lines.end()) {
3903 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3905 /* j is the line that this audio range starts in; fade into it;
3906 64 samples length plucked out of thin air.
3909 framepos_t a = i->start + 64;
3914 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3915 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3917 the_list->add (p, the_list->eval (p));
3918 j->line->add_always_in_view (p);
3919 the_list->add (q, the_list->eval (q));
3920 j->line->add_always_in_view (q);
3923 /* same thing for the end */
3926 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3930 if (j != _lines.end()) {
3931 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3933 /* j is the line that this audio range starts in; fade out of it;
3934 64 samples length plucked out of thin air.
3937 framepos_t b = i->end - 64;
3942 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3943 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
3945 the_list->add (p, the_list->eval (p));
3946 j->line->add_always_in_view (p);
3947 the_list->add (q, the_list->eval (q));
3948 j->line->add_always_in_view (q);
3952 _nothing_to_drag = true;
3954 /* Find all the points that should be dragged and put them in the relevant
3955 points lists in the Line structs.
3958 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3960 uint32_t const N = i->line->npoints ();
3961 for (uint32_t j = 0; j < N; ++j) {
3963 /* here's a control point on this line */
3964 ControlPoint* p = i->line->nth (j);
3965 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
3967 /* see if it's inside a range */
3968 list<AudioRange>::const_iterator k = _ranges.begin ();
3969 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
3973 if (k != _ranges.end()) {
3974 /* dragging this point */
3975 _nothing_to_drag = false;
3976 i->points.push_back (p);
3982 if (_nothing_to_drag) {
3986 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3987 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
3992 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
3994 if (_nothing_to_drag) {
3998 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3999 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4001 /* we are ignoring x position for this drag, so we can just pass in anything */
4002 i->line->drag_motion (0, f, true, false);
4007 AutomationRangeDrag::finished (GdkEvent* event, bool)
4009 if (_nothing_to_drag) {
4013 motion (event, false);
4014 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4015 i->line->end_drag ();
4016 i->line->clear_always_in_view ();
4019 _editor->session()->commit_reversible_command ();
4023 AutomationRangeDrag::aborted ()
4025 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4026 i->line->clear_always_in_view ();
4031 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4034 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4035 layer = v->region()->layer ();
4036 initial_y = v->get_canvas_group()->property_y ();
4037 initial_playlist = v->region()->playlist ();
4038 initial_position = v->region()->position ();
4039 initial_end = v->region()->position () + v->region()->length ();