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::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_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::CanvasNote*>(_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::CanvasNote*>(_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 _operation = ContentsTrim;
1537 Drag::start_grab (event, _editor->cursors()->trimmer);
1539 /* These will get overridden for a point trim.*/
1540 if (pf < (region_start + region_length/2)) {
1541 /* closer to front */
1542 _operation = StartTrim;
1543 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1546 _operation = EndTrim;
1547 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1551 switch (_operation) {
1553 _editor->show_verbose_time_cursor (region_start, 10);
1554 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1555 i->view->trim_front_starting ();
1559 _editor->show_verbose_time_cursor (region_end, 10);
1562 _editor->show_verbose_time_cursor (pf, 10);
1566 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1567 i->view->region()->suspend_property_changes ();
1572 TrimDrag::motion (GdkEvent* event, bool first_move)
1574 RegionView* rv = _primary;
1577 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1578 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1579 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1581 if (tv && tv->is_track()) {
1582 speed = tv->track()->speed();
1585 framecnt_t const dt = adjusted_current_frame (event) - grab_frame ();
1591 switch (_operation) {
1593 trim_type = "Region start trim";
1596 trim_type = "Region end trim";
1599 trim_type = "Region content trim";
1603 _editor->begin_reversible_command (trim_type);
1604 _have_transaction = true;
1606 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1607 RegionView* rv = i->view;
1608 rv->fake_set_opaque (false);
1609 rv->enable_display (false);
1610 rv->region()->clear_changes ();
1612 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1615 arv->temporarily_hide_envelope ();
1618 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1619 insert_result = _editor->motion_frozen_playlists.insert (pl);
1621 if (insert_result.second) {
1627 bool non_overlap_trim = false;
1629 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1630 non_overlap_trim = true;
1633 switch (_operation) {
1635 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1636 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1641 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1642 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1648 bool swap_direction = false;
1650 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1651 swap_direction = true;
1654 framecnt_t frame_delta = 0;
1656 bool left_direction = false;
1657 if (last_pointer_frame() > adjusted_current_frame(event)) {
1658 left_direction = true;
1661 if (left_direction) {
1662 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1664 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1667 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1668 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1674 switch (_operation) {
1676 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->position() / speed), 10);
1679 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->last_frame() / speed), 10);
1682 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1689 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1691 if (movement_occurred) {
1692 motion (event, false);
1694 if (!_editor->selection->selected (_primary)) {
1695 _primary->thaw_after_trim ();
1698 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1699 i->view->thaw_after_trim ();
1700 i->view->enable_display (true);
1701 i->view->fake_set_opaque (true);
1702 if (_have_transaction) {
1703 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1707 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1711 _editor->motion_frozen_playlists.clear ();
1713 if (_have_transaction) {
1714 _editor->commit_reversible_command();
1718 /* no mouse movement */
1719 _editor->point_trim (event, adjusted_current_frame (event));
1722 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1723 if (_operation == StartTrim) {
1724 i->view->trim_front_ending ();
1726 i->view->region()->resume_property_changes ();
1731 TrimDrag::aborted ()
1733 /* Our motion method is changing model state, so use the Undo system
1734 to cancel. Perhaps not ideal, as this will leave an Undo point
1735 behind which may be slightly odd from the user's point of view.
1740 if (_have_transaction) {
1744 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1745 i->view->region()->resume_property_changes ();
1749 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1753 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1755 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1760 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1763 // create a dummy marker for visual representation of moving the copy.
1764 // The actual copying is not done before we reach the finish callback.
1766 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1768 MeterMarker* new_marker = new MeterMarker (
1770 *_editor->meter_group,
1771 *_editor->cursor_group,
1772 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1774 *new MeterSection (_marker->meter())
1777 _item = &new_marker->the_item ();
1778 _marker = new_marker;
1782 MetricSection& section (_marker->meter());
1784 if (!section.movable()) {
1790 Drag::start_grab (event, cursor);
1792 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1794 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1798 MeterMarkerDrag::motion (GdkEvent* event, bool)
1800 framepos_t const pf = adjusted_current_frame (event);
1802 _marker->set_position (pf);
1804 _editor->show_verbose_time_cursor (pf, 10);
1808 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1810 if (!movement_occurred) {
1814 motion (event, false);
1818 TempoMap& map (_editor->session()->tempo_map());
1819 map.bbt_time (last_pointer_frame(), when);
1821 if (_copy == true) {
1822 _editor->begin_reversible_command (_("copy meter mark"));
1823 XMLNode &before = map.get_state();
1824 map.add_meter (_marker->meter(), when);
1825 XMLNode &after = map.get_state();
1826 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1827 _editor->commit_reversible_command ();
1829 // delete the dummy marker we used for visual representation of copying.
1830 // a new visual marker will show up automatically.
1833 _editor->begin_reversible_command (_("move meter mark"));
1834 XMLNode &before = map.get_state();
1835 map.move_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 ();
1843 MeterMarkerDrag::aborted ()
1845 _marker->set_position (_marker->meter().frame ());
1848 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1852 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1854 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1859 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1863 // create a dummy marker for visual representation of moving the copy.
1864 // The actual copying is not done before we reach the finish callback.
1866 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1868 TempoMarker* new_marker = new TempoMarker (
1870 *_editor->tempo_group,
1871 *_editor->cursor_group,
1872 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1874 *new TempoSection (_marker->tempo())
1877 _item = &new_marker->the_item ();
1878 _marker = new_marker;
1882 Drag::start_grab (event, cursor);
1884 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1885 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1889 TempoMarkerDrag::motion (GdkEvent* event, bool)
1891 framepos_t const pf = adjusted_current_frame (event);
1892 _marker->set_position (pf);
1893 _editor->show_verbose_time_cursor (pf, 10);
1897 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1899 if (!movement_occurred) {
1903 motion (event, false);
1907 TempoMap& map (_editor->session()->tempo_map());
1908 map.bbt_time (last_pointer_frame(), when);
1910 if (_copy == true) {
1911 _editor->begin_reversible_command (_("copy tempo mark"));
1912 XMLNode &before = map.get_state();
1913 map.add_tempo (_marker->tempo(), when);
1914 XMLNode &after = map.get_state();
1915 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1916 _editor->commit_reversible_command ();
1918 // delete the dummy marker we used for visual representation of copying.
1919 // a new visual marker will show up automatically.
1922 _editor->begin_reversible_command (_("move tempo mark"));
1923 XMLNode &before = map.get_state();
1924 map.move_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 ();
1932 TempoMarkerDrag::aborted ()
1934 _marker->set_position (_marker->tempo().frame());
1937 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1941 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
1943 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1948 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1950 Drag::start_grab (event, c);
1954 framepos_t where = _editor->event_frame (event, 0, 0);
1956 _editor->snap_to_with_modifier (where, event);
1957 _editor->playhead_cursor->set_position (where);
1961 if (_cursor == _editor->playhead_cursor) {
1962 _editor->_dragging_playhead = true;
1964 Session* s = _editor->session ();
1967 if (_was_rolling && _stop) {
1971 if (s->is_auditioning()) {
1972 s->cancel_audition ();
1975 s->request_suspend_timecode_transmission ();
1977 if (s->timecode_transmission_suspended ()) {
1978 framepos_t const f = _editor->playhead_cursor->current_frame;
1979 s->send_mmc_locate (f);
1980 s->send_full_time_code (f);
1985 _pointer_frame_offset = raw_grab_frame() - _cursor->current_frame;
1987 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1991 CursorDrag::motion (GdkEvent* event, bool)
1993 framepos_t const adjusted_frame = adjusted_current_frame (event);
1995 if (adjusted_frame == last_pointer_frame()) {
1999 _cursor->set_position (adjusted_frame);
2001 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2003 Session* s = _editor->session ();
2004 if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
2005 framepos_t const f = _editor->playhead_cursor->current_frame;
2006 s->send_mmc_locate (f);
2007 s->send_full_time_code (f);
2012 _editor->update_canvas_now ();
2014 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2018 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2020 _editor->_dragging_playhead = false;
2022 if (!movement_occurred && _stop) {
2026 motion (event, false);
2028 if (_item == &_editor->playhead_cursor->canvas_item) {
2029 Session* s = _editor->session ();
2031 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2032 _editor->_pending_locate_request = true;
2033 s->request_resume_timecode_transmission ();
2039 CursorDrag::aborted ()
2041 if (_editor->_dragging_playhead) {
2042 _editor->session()->request_resume_timecode_transmission ();
2043 _editor->_dragging_playhead = false;
2046 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2049 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2050 : RegionDrag (e, i, p, v)
2052 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2056 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2058 Drag::start_grab (event, cursor);
2060 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2061 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2063 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2064 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2066 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2070 FadeInDrag::motion (GdkEvent* event, bool)
2072 framecnt_t fade_length;
2074 framepos_t const pos = adjusted_current_frame (event);
2076 boost::shared_ptr<Region> region = _primary->region ();
2078 if (pos < (region->position() + 64)) {
2079 fade_length = 64; // this should be a minimum defined somewhere
2080 } else if (pos > region->last_frame()) {
2081 fade_length = region->length();
2083 fade_length = pos - region->position();
2086 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2088 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2094 tmp->reset_fade_in_shape_width (fade_length);
2095 tmp->show_fade_line((framecnt_t) fade_length);
2098 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2102 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2104 if (!movement_occurred) {
2108 framecnt_t fade_length;
2110 framepos_t const pos = adjusted_current_frame (event);
2112 boost::shared_ptr<Region> region = _primary->region ();
2114 if (pos < (region->position() + 64)) {
2115 fade_length = 64; // this should be a minimum defined somewhere
2116 } else if (pos > region->last_frame()) {
2117 fade_length = region->length();
2119 fade_length = pos - region->position();
2122 _editor->begin_reversible_command (_("change fade in length"));
2124 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2126 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2132 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2133 XMLNode &before = alist->get_state();
2135 tmp->audio_region()->set_fade_in_length (fade_length);
2136 tmp->audio_region()->set_fade_in_active (true);
2137 tmp->hide_fade_line();
2139 XMLNode &after = alist->get_state();
2140 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2143 _editor->commit_reversible_command ();
2147 FadeInDrag::aborted ()
2149 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2150 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2156 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2157 tmp->hide_fade_line();
2161 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2162 : RegionDrag (e, i, p, v)
2164 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2168 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2170 Drag::start_grab (event, cursor);
2172 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2173 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2175 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2176 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2178 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2182 FadeOutDrag::motion (GdkEvent* event, bool)
2184 framecnt_t fade_length;
2186 framepos_t const pos = adjusted_current_frame (event);
2188 boost::shared_ptr<Region> region = _primary->region ();
2190 if (pos > (region->last_frame() - 64)) {
2191 fade_length = 64; // this should really be a minimum fade defined somewhere
2193 else if (pos < region->position()) {
2194 fade_length = region->length();
2197 fade_length = region->last_frame() - pos;
2200 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2202 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2208 tmp->reset_fade_out_shape_width (fade_length);
2209 tmp->show_fade_line(region->length() - fade_length);
2212 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2216 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2218 if (!movement_occurred) {
2222 framecnt_t fade_length;
2224 framepos_t const pos = adjusted_current_frame (event);
2226 boost::shared_ptr<Region> region = _primary->region ();
2228 if (pos > (region->last_frame() - 64)) {
2229 fade_length = 64; // this should really be a minimum fade defined somewhere
2231 else if (pos < region->position()) {
2232 fade_length = region->length();
2235 fade_length = region->last_frame() - pos;
2238 _editor->begin_reversible_command (_("change fade out length"));
2240 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2242 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2248 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2249 XMLNode &before = alist->get_state();
2251 tmp->audio_region()->set_fade_out_length (fade_length);
2252 tmp->audio_region()->set_fade_out_active (true);
2253 tmp->hide_fade_line();
2255 XMLNode &after = alist->get_state();
2256 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2259 _editor->commit_reversible_command ();
2263 FadeOutDrag::aborted ()
2265 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2266 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2272 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2273 tmp->hide_fade_line();
2277 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2280 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2282 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2285 _points.push_back (Gnome::Art::Point (0, 0));
2286 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2288 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2289 _line->property_width_pixels() = 1;
2290 _line->property_points () = _points;
2293 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2296 MarkerDrag::~MarkerDrag ()
2298 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2304 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2306 Drag::start_grab (event, cursor);
2310 Location *location = _editor->find_location_from_marker (_marker, is_start);
2311 _editor->_dragging_edit_point = true;
2313 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2315 update_item (location);
2317 // _drag_line->show();
2318 // _line->raise_to_top();
2321 _editor->show_verbose_time_cursor (location->start(), 10);
2323 _editor->show_verbose_time_cursor (location->end(), 10);
2326 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2329 case Selection::Toggle:
2330 _editor->selection->toggle (_marker);
2332 case Selection::Set:
2333 if (!_editor->selection->selected (_marker)) {
2334 _editor->selection->set (_marker);
2337 case Selection::Extend:
2339 Locations::LocationList ll;
2340 list<Marker*> to_add;
2342 _editor->selection->markers.range (s, e);
2343 s = min (_marker->position(), s);
2344 e = max (_marker->position(), e);
2347 if (e < max_framepos) {
2350 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2351 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2352 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2355 to_add.push_back (lm->start);
2358 to_add.push_back (lm->end);
2362 if (!to_add.empty()) {
2363 _editor->selection->add (to_add);
2367 case Selection::Add:
2368 _editor->selection->add (_marker);
2372 /* Set up copies for us to manipulate during the drag */
2374 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2375 Location* l = _editor->find_location_from_marker (*i, is_start);
2376 _copied_locations.push_back (new Location (*l));
2381 MarkerDrag::motion (GdkEvent* event, bool)
2383 framecnt_t f_delta = 0;
2385 bool move_both = false;
2387 Location *real_location;
2388 Location *copy_location = 0;
2390 framepos_t const newframe = adjusted_current_frame (event);
2392 framepos_t next = newframe;
2394 if (newframe == last_pointer_frame()) {
2398 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2402 MarkerSelection::iterator i;
2403 list<Location*>::iterator x;
2405 /* find the marker we're dragging, and compute the delta */
2407 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2408 x != _copied_locations.end() && i != _editor->selection->markers.end();
2414 if (marker == _marker) {
2416 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2421 if (real_location->is_mark()) {
2422 f_delta = newframe - copy_location->start();
2426 switch (marker->type()) {
2427 case Marker::SessionStart:
2428 case Marker::RangeStart:
2429 case Marker::LoopStart:
2430 case Marker::PunchIn:
2431 f_delta = newframe - copy_location->start();
2434 case Marker::SessionEnd:
2435 case Marker::RangeEnd:
2436 case Marker::LoopEnd:
2437 case Marker::PunchOut:
2438 f_delta = newframe - copy_location->end();
2441 /* what kind of marker is this ? */
2449 if (i == _editor->selection->markers.end()) {
2450 /* hmm, impossible - we didn't find the dragged marker */
2454 /* now move them all */
2456 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2457 x != _copied_locations.end() && i != _editor->selection->markers.end();
2463 /* call this to find out if its the start or end */
2465 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2469 if (real_location->locked()) {
2473 if (copy_location->is_mark()) {
2477 copy_location->set_start (copy_location->start() + f_delta);
2481 framepos_t new_start = copy_location->start() + f_delta;
2482 framepos_t new_end = copy_location->end() + f_delta;
2484 if (is_start) { // start-of-range marker
2487 copy_location->set_start (new_start);
2488 copy_location->set_end (new_end);
2489 } else if (new_start < copy_location->end()) {
2490 copy_location->set_start (new_start);
2492 _editor->snap_to (next, 1, true);
2493 copy_location->set_end (next);
2494 copy_location->set_start (newframe);
2497 } else { // end marker
2500 copy_location->set_end (new_end);
2501 copy_location->set_start (new_start);
2502 } else if (new_end > copy_location->start()) {
2503 copy_location->set_end (new_end);
2504 } else if (newframe > 0) {
2505 _editor->snap_to (next, -1, true);
2506 copy_location->set_start (next);
2507 copy_location->set_end (newframe);
2512 update_item (copy_location);
2514 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2517 lm->set_position (copy_location->start(), copy_location->end());
2521 assert (!_copied_locations.empty());
2523 _editor->show_verbose_time_cursor (newframe, 10);
2526 _editor->update_canvas_now ();
2531 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2533 if (!movement_occurred) {
2535 /* just a click, do nothing but finish
2536 off the selection process
2539 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2542 case Selection::Set:
2543 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2544 _editor->selection->set (_marker);
2548 case Selection::Toggle:
2549 case Selection::Extend:
2550 case Selection::Add:
2557 _editor->_dragging_edit_point = false;
2559 _editor->begin_reversible_command ( _("move marker") );
2560 XMLNode &before = _editor->session()->locations()->get_state();
2562 MarkerSelection::iterator i;
2563 list<Location*>::iterator x;
2566 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2567 x != _copied_locations.end() && i != _editor->selection->markers.end();
2570 Location * location = _editor->find_location_from_marker (*i, is_start);
2574 if (location->locked()) {
2578 if (location->is_mark()) {
2579 location->set_start ((*x)->start());
2581 location->set ((*x)->start(), (*x)->end());
2586 XMLNode &after = _editor->session()->locations()->get_state();
2587 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2588 _editor->commit_reversible_command ();
2594 MarkerDrag::aborted ()
2600 MarkerDrag::update_item (Location* location)
2602 double const x1 = _editor->frame_to_pixel (location->start());
2604 _points.front().set_x(x1);
2605 _points.back().set_x(x1);
2606 _line->property_points() = _points;
2609 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2611 _cumulative_x_drag (0),
2612 _cumulative_y_drag (0)
2614 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2616 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2622 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2624 Drag::start_grab (event, _editor->cursors()->fader);
2626 // start the grab at the center of the control point so
2627 // the point doesn't 'jump' to the mouse after the first drag
2628 _fixed_grab_x = _point->get_x();
2629 _fixed_grab_y = _point->get_y();
2631 float const fraction = 1 - (_point->get_y() / _point->line().height());
2633 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2635 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2636 event->button.x + 10, event->button.y + 10);
2638 _editor->show_verbose_canvas_cursor ();
2642 ControlPointDrag::motion (GdkEvent* event, bool)
2644 double dx = _drags->current_pointer_x() - last_pointer_x();
2645 double dy = _drags->current_pointer_y() - last_pointer_y();
2647 if (event->button.state & Keyboard::SecondaryModifier) {
2652 /* coordinate in pixels relative to the start of the region (for region-based automation)
2653 or track (for track-based automation) */
2654 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2655 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2657 // calculate zero crossing point. back off by .01 to stay on the
2658 // positive side of zero
2659 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2661 // make sure we hit zero when passing through
2662 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2666 if (_x_constrained) {
2669 if (_y_constrained) {
2673 _cumulative_x_drag = cx - _fixed_grab_x;
2674 _cumulative_y_drag = cy - _fixed_grab_y;
2678 cy = min ((double) _point->line().height(), cy);
2680 framepos_t cx_frames = _editor->unit_to_frame (cx);
2682 if (!_x_constrained) {
2683 _editor->snap_to_with_modifier (cx_frames, event);
2686 cx_frames = min (cx_frames, _point->line().maximum_time());
2688 float const fraction = 1.0 - (cy / _point->line().height());
2690 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2692 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2694 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2698 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2700 if (!movement_occurred) {
2704 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2705 _editor->reset_point_selection ();
2709 motion (event, false);
2712 _point->line().end_drag ();
2713 _editor->session()->commit_reversible_command ();
2717 ControlPointDrag::aborted ()
2719 _point->line().reset ();
2723 ControlPointDrag::active (Editing::MouseMode m)
2725 if (m == Editing::MouseGain) {
2726 /* always active in mouse gain */
2730 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2731 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2734 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2737 _cumulative_y_drag (0)
2739 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2743 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2745 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2748 _item = &_line->grab_item ();
2750 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2751 origin, and ditto for y.
2754 double cx = event->button.x;
2755 double cy = event->button.y;
2757 _line->parent_group().w2i (cx, cy);
2759 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2764 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2765 /* no adjacent points */
2769 Drag::start_grab (event, _editor->cursors()->fader);
2771 /* store grab start in parent frame */
2776 double fraction = 1.0 - (cy / _line->height());
2778 _line->start_drag_line (before, after, fraction);
2780 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2781 event->button.x + 10, event->button.y + 10);
2783 _editor->show_verbose_canvas_cursor ();
2787 LineDrag::motion (GdkEvent* event, bool)
2789 double dy = _drags->current_pointer_y() - last_pointer_y();
2791 if (event->button.state & Keyboard::SecondaryModifier) {
2795 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2797 _cumulative_y_drag = cy - _fixed_grab_y;
2800 cy = min ((double) _line->height(), cy);
2802 double const fraction = 1.0 - (cy / _line->height());
2806 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2812 /* we are ignoring x position for this drag, so we can just pass in anything */
2813 _line->drag_motion (0, fraction, true, push);
2815 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2819 LineDrag::finished (GdkEvent* event, bool)
2821 motion (event, false);
2823 _editor->session()->commit_reversible_command ();
2827 LineDrag::aborted ()
2832 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2835 _cumulative_x_drag (0)
2837 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2841 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2843 Drag::start_grab (event);
2845 _line = reinterpret_cast<SimpleLine*> (_item);
2848 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2850 double cx = event->button.x;
2851 double cy = event->button.y;
2853 _item->property_parent().get_value()->w2i(cx, cy);
2855 /* store grab start in parent frame */
2856 _region_view_grab_x = cx;
2858 _before = _line->property_x1();
2860 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2862 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2866 FeatureLineDrag::motion (GdkEvent*, bool)
2868 double dx = _drags->current_pointer_x() - last_pointer_x();
2870 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2872 _cumulative_x_drag += dx;
2874 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2883 _line->property_x1() = cx;
2884 _line->property_x2() = cx;
2886 _before = _line->property_x1();
2890 FeatureLineDrag::finished (GdkEvent*, bool)
2892 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2893 _arv->update_transient(_before, _line->property_x1());
2897 FeatureLineDrag::aborted ()
2902 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2905 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2909 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2911 Drag::start_grab (event);
2912 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2916 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2923 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2925 framepos_t grab = grab_frame ();
2926 if (Config->get_rubberbanding_snaps_to_grid ()) {
2927 _editor->snap_to_with_modifier (grab, event);
2930 /* base start and end on initial click position */
2940 if (_drags->current_pointer_y() < grab_y()) {
2941 y1 = _drags->current_pointer_y();
2944 y2 = _drags->current_pointer_y();
2949 if (start != end || y1 != y2) {
2951 double x1 = _editor->frame_to_pixel (start);
2952 double x2 = _editor->frame_to_pixel (end);
2954 _editor->rubberband_rect->property_x1() = x1;
2955 _editor->rubberband_rect->property_y1() = y1;
2956 _editor->rubberband_rect->property_x2() = x2;
2957 _editor->rubberband_rect->property_y2() = y2;
2959 _editor->rubberband_rect->show();
2960 _editor->rubberband_rect->raise_to_top();
2962 _editor->show_verbose_time_cursor (pf, 10);
2967 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2969 if (movement_occurred) {
2971 motion (event, false);
2974 if (_drags->current_pointer_y() < grab_y()) {
2975 y1 = _drags->current_pointer_y();
2978 y2 = _drags->current_pointer_y();
2983 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2986 _editor->begin_reversible_command (_("rubberband selection"));
2988 if (grab_frame() < last_pointer_frame()) {
2989 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
2991 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
2995 _editor->commit_reversible_command ();
2999 if (!getenv("ARDOUR_SAE")) {
3000 _editor->selection->clear_tracks();
3002 _editor->selection->clear_regions();
3003 _editor->selection->clear_points ();
3004 _editor->selection->clear_lines ();
3007 _editor->rubberband_rect->hide();
3011 RubberbandSelectDrag::aborted ()
3013 _editor->rubberband_rect->hide ();
3016 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3017 : RegionDrag (e, i, p, v)
3019 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3023 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3025 Drag::start_grab (event, cursor);
3027 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3031 TimeFXDrag::motion (GdkEvent* event, bool)
3033 RegionView* rv = _primary;
3035 framepos_t const pf = adjusted_current_frame (event);
3037 if (pf > rv->region()->position()) {
3038 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3041 _editor->show_verbose_time_cursor (pf, 10);
3045 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3047 _primary->get_time_axis_view().hide_timestretch ();
3049 if (!movement_occurred) {
3053 if (last_pointer_frame() < _primary->region()->position()) {
3054 /* backwards drag of the left edge - not usable */
3058 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3060 float percentage = (double) newlen / (double) _primary->region()->length();
3062 #ifndef USE_RUBBERBAND
3063 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3064 if (_primary->region()->data_type() == DataType::AUDIO) {
3065 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3069 _editor->begin_reversible_command (_("timestretch"));
3071 // XXX how do timeFX on multiple regions ?
3076 if (_editor->time_stretch (rs, percentage) == -1) {
3077 error << _("An error occurred while executing time stretch operation") << endmsg;
3082 TimeFXDrag::aborted ()
3084 _primary->get_time_axis_view().hide_timestretch ();
3087 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3090 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3094 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3096 Drag::start_grab (event);
3100 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3102 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3106 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3108 if (movement_occurred && _editor->session()) {
3109 /* make sure we stop */
3110 _editor->session()->request_transport_speed (0.0);
3115 ScrubDrag::aborted ()
3120 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3124 , _original_pointer_time_axis (-1)
3125 , _last_pointer_time_axis (-1)
3127 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3131 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3133 framepos_t start = 0;
3136 if (_editor->session() == 0) {
3140 Gdk::Cursor* cursor = 0;
3142 switch (_operation) {
3143 case CreateSelection:
3144 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3149 cursor = _editor->cursors()->selector;
3150 Drag::start_grab (event, cursor);
3153 case SelectionStartTrim:
3154 if (_editor->clicked_axisview) {
3155 _editor->clicked_axisview->order_selection_trims (_item, true);
3157 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3158 start = _editor->selection->time[_editor->clicked_selection].start;
3159 _pointer_frame_offset = raw_grab_frame() - start;
3162 case SelectionEndTrim:
3163 if (_editor->clicked_axisview) {
3164 _editor->clicked_axisview->order_selection_trims (_item, false);
3166 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3167 end = _editor->selection->time[_editor->clicked_selection].end;
3168 _pointer_frame_offset = raw_grab_frame() - end;
3172 start = _editor->selection->time[_editor->clicked_selection].start;
3173 Drag::start_grab (event, cursor);
3174 _pointer_frame_offset = raw_grab_frame() - start;
3178 if (_operation == SelectionMove) {
3179 _editor->show_verbose_time_cursor (start, 10);
3181 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3184 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3188 SelectionDrag::motion (GdkEvent* event, bool first_move)
3190 framepos_t start = 0;
3194 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3195 if (pending_time_axis.first == 0) {
3199 framepos_t const pending_position = adjusted_current_frame (event);
3201 /* only alter selection if things have changed */
3203 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3207 switch (_operation) {
3208 case CreateSelection:
3210 framepos_t grab = grab_frame ();
3213 _editor->snap_to (grab);
3216 if (pending_position < grab_frame()) {
3217 start = pending_position;
3220 end = pending_position;
3224 /* first drag: Either add to the selection
3225 or create a new selection
3231 /* adding to the selection */
3232 _editor->set_selected_track_as_side_effect (Selection::Add);
3233 //_editor->selection->add (_editor->clicked_axisview);
3234 _editor->clicked_selection = _editor->selection->add (start, end);
3239 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3240 //_editor->selection->set (_editor->clicked_axisview);
3241 _editor->set_selected_track_as_side_effect (Selection::Set);
3244 _editor->clicked_selection = _editor->selection->set (start, end);
3248 /* select the track that we're in */
3249 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3250 // _editor->set_selected_track_as_side_effect (Selection::Add);
3251 _editor->selection->add (pending_time_axis.first);
3252 _added_time_axes.push_back (pending_time_axis.first);
3255 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3256 tracks that we selected in the first place.
3259 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3260 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3262 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3263 while (i != _added_time_axes.end()) {
3265 list<TimeAxisView*>::iterator tmp = i;
3268 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3269 _editor->selection->remove (*i);
3270 _added_time_axes.remove (*i);
3279 case SelectionStartTrim:
3281 start = _editor->selection->time[_editor->clicked_selection].start;
3282 end = _editor->selection->time[_editor->clicked_selection].end;
3284 if (pending_position > end) {
3287 start = pending_position;
3291 case SelectionEndTrim:
3293 start = _editor->selection->time[_editor->clicked_selection].start;
3294 end = _editor->selection->time[_editor->clicked_selection].end;
3296 if (pending_position < start) {
3299 end = pending_position;
3306 start = _editor->selection->time[_editor->clicked_selection].start;
3307 end = _editor->selection->time[_editor->clicked_selection].end;
3309 length = end - start;
3311 start = pending_position;
3312 _editor->snap_to (start);
3314 end = start + length;
3319 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3320 _editor->start_canvas_autoscroll (1, 0);
3324 _editor->selection->replace (_editor->clicked_selection, start, end);
3327 if (_operation == SelectionMove) {
3328 _editor->show_verbose_time_cursor(start, 10);
3330 _editor->show_verbose_time_cursor(pending_position, 10);
3335 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3337 Session* s = _editor->session();
3339 if (movement_occurred) {
3340 motion (event, false);
3341 /* XXX this is not object-oriented programming at all. ick */
3342 if (_editor->selection->time.consolidate()) {
3343 _editor->selection->TimeChanged ();
3346 /* XXX what if its a music time selection? */
3347 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3348 s->request_play_range (&_editor->selection->time, true);
3353 /* just a click, no pointer movement.*/
3355 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3356 _editor->selection->clear_time();
3359 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3360 _editor->selection->set (_editor->clicked_axisview);
3363 if (s && s->get_play_range () && s->transport_rolling()) {
3364 s->request_stop (false, false);
3369 _editor->stop_canvas_autoscroll ();
3373 SelectionDrag::aborted ()
3378 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3383 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3385 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3386 physical_screen_height (_editor->get_window()));
3387 _drag_rect->hide ();
3389 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3390 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3394 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3396 if (_editor->session() == 0) {
3400 Gdk::Cursor* cursor = 0;
3402 if (!_editor->temp_location) {
3403 _editor->temp_location = new Location (*_editor->session());
3406 switch (_operation) {
3407 case CreateRangeMarker:
3408 case CreateTransportMarker:
3409 case CreateCDMarker:
3411 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3416 cursor = _editor->cursors()->selector;
3420 Drag::start_grab (event, cursor);
3422 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3426 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3428 framepos_t start = 0;
3430 ArdourCanvas::SimpleRect *crect;
3432 switch (_operation) {
3433 case CreateRangeMarker:
3434 crect = _editor->range_bar_drag_rect;
3436 case CreateTransportMarker:
3437 crect = _editor->transport_bar_drag_rect;
3439 case CreateCDMarker:
3440 crect = _editor->cd_marker_bar_drag_rect;
3443 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3448 framepos_t const pf = adjusted_current_frame (event);
3450 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3451 framepos_t grab = grab_frame ();
3452 _editor->snap_to (grab);
3454 if (pf < grab_frame()) {
3462 /* first drag: Either add to the selection
3463 or create a new selection.
3468 _editor->temp_location->set (start, end);
3472 update_item (_editor->temp_location);
3474 //_drag_rect->raise_to_top();
3479 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3480 _editor->start_canvas_autoscroll (1, 0);
3484 _editor->temp_location->set (start, end);
3486 double x1 = _editor->frame_to_pixel (start);
3487 double x2 = _editor->frame_to_pixel (end);
3488 crect->property_x1() = x1;
3489 crect->property_x2() = x2;
3491 update_item (_editor->temp_location);
3494 _editor->show_verbose_time_cursor (pf, 10);
3499 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3501 Location * newloc = 0;
3505 if (movement_occurred) {
3506 motion (event, false);
3509 switch (_operation) {
3510 case CreateRangeMarker:
3511 case CreateCDMarker:
3513 _editor->begin_reversible_command (_("new range marker"));
3514 XMLNode &before = _editor->session()->locations()->get_state();
3515 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3516 if (_operation == CreateCDMarker) {
3517 flags = Location::IsRangeMarker | Location::IsCDMarker;
3518 _editor->cd_marker_bar_drag_rect->hide();
3521 flags = Location::IsRangeMarker;
3522 _editor->range_bar_drag_rect->hide();
3524 newloc = new Location (
3525 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3528 _editor->session()->locations()->add (newloc, true);
3529 XMLNode &after = _editor->session()->locations()->get_state();
3530 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3531 _editor->commit_reversible_command ();
3535 case CreateTransportMarker:
3536 // popup menu to pick loop or punch
3537 _editor->new_transport_marker_context_menu (&event->button, _item);
3541 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3543 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3548 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3550 if (end == max_framepos) {
3551 end = _editor->session()->current_end_frame ();
3554 if (start == max_framepos) {
3555 start = _editor->session()->current_start_frame ();
3558 switch (_editor->mouse_mode) {
3560 /* find the two markers on either side and then make the selection from it */
3561 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3565 /* find the two markers on either side of the click and make the range out of it */
3566 _editor->selection->set (start, end);
3575 _editor->stop_canvas_autoscroll ();
3579 RangeMarkerBarDrag::aborted ()
3585 RangeMarkerBarDrag::update_item (Location* location)
3587 double const x1 = _editor->frame_to_pixel (location->start());
3588 double const x2 = _editor->frame_to_pixel (location->end());
3590 _drag_rect->property_x1() = x1;
3591 _drag_rect->property_x2() = x2;
3594 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3598 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3602 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3604 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3605 Drag::start_grab (event, _editor->cursors()->zoom_out);
3608 Drag::start_grab (event, _editor->cursors()->zoom_in);
3612 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3616 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3621 framepos_t const pf = adjusted_current_frame (event);
3623 framepos_t grab = grab_frame ();
3624 _editor->snap_to_with_modifier (grab, event);
3626 /* base start and end on initial click position */
3638 _editor->zoom_rect->show();
3639 _editor->zoom_rect->raise_to_top();
3642 _editor->reposition_zoom_rect(start, end);
3644 _editor->show_verbose_time_cursor (pf, 10);
3649 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3651 if (movement_occurred) {
3652 motion (event, false);
3654 if (grab_frame() < last_pointer_frame()) {
3655 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3657 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3660 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3663 _editor->zoom_rect->hide();
3667 MouseZoomDrag::aborted ()
3669 _editor->zoom_rect->hide ();
3672 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3674 , _cumulative_dx (0)
3675 , _cumulative_dy (0)
3677 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3679 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3680 _region = &_primary->region_view ();
3681 _note_height = _region->midi_stream_view()->note_height ();
3685 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3687 Drag::start_grab (event);
3689 if (!(_was_selected = _primary->selected())) {
3691 /* tertiary-click means extend selection - we'll do that on button release,
3692 so don't add it here, because otherwise we make it hard to figure
3693 out the "extend-to" range.
3696 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3699 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3702 _region->note_selected (_primary, true);
3704 _region->unique_select (_primary);
3710 /** @return Current total drag x change in frames */
3712 NoteDrag::total_dx () const
3715 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3717 /* primary note time */
3718 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3720 /* new time of the primary note relative to the region position */
3721 frameoffset_t const st = n + dx;
3723 /* snap and return corresponding delta */
3724 return _region->snap_frame_to_frame (st) - n;
3727 /** @return Current total drag y change in notes */
3729 NoteDrag::total_dy () const
3731 /* this is `backwards' to make increasing note number go in the right direction */
3732 double const dy = _drags->current_pointer_y() - grab_y();
3737 if (abs (dy) >= _note_height) {
3739 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3741 ndy = (int8_t) floor (dy / _note_height / 2.0);
3745 /* more positive value = higher pitch and higher y-axis position on track,
3746 which is the inverse of the X-centric geometric universe
3753 NoteDrag::motion (GdkEvent *, bool)
3755 /* Total change in x and y since the start of the drag */
3756 frameoffset_t const dx = total_dx ();
3757 int8_t const dy = -total_dy ();
3759 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3760 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3761 double const tdy = dy * _note_height - _cumulative_dy;
3764 _cumulative_dx += tdx;
3765 _cumulative_dy += tdy;
3767 int8_t note_delta = total_dy();
3769 _region->move_selection (tdx, tdy, note_delta);
3772 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3773 (int) floor (_primary->note()->note() + note_delta));
3775 _editor->show_verbose_canvas_cursor_with (buf);
3780 NoteDrag::finished (GdkEvent* ev, bool moved)
3783 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3785 if (_was_selected) {
3786 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3788 _region->note_deselected (_primary);
3791 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3792 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3794 if (!extend && !add && _region->selection_size() > 1) {
3795 _region->unique_select (_primary);
3796 } else if (extend) {
3797 _region->note_selected (_primary, true, true);
3799 /* it was added during button press */
3804 _region->note_dropped (_primary, total_dx(), total_dy());
3809 NoteDrag::aborted ()
3814 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3815 : Drag (editor, item)
3817 , _nothing_to_drag (false)
3819 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3821 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3824 /* get all lines in the automation view */
3825 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3827 /* find those that overlap the ranges being dragged */
3828 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3829 while (i != lines.end ()) {
3830 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3833 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3835 /* check this range against all the AudioRanges that we are using */
3836 list<AudioRange>::const_iterator k = _ranges.begin ();
3837 while (k != _ranges.end()) {
3838 if (k->coverage (r.first, r.second) != OverlapNone) {
3844 /* add it to our list if it overlaps at all */
3845 if (k != _ranges.end()) {
3850 _lines.push_back (n);
3856 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3860 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3862 Drag::start_grab (event, cursor);
3864 /* Get line states before we start changing things */
3865 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3866 i->state = &i->line->get_state ();
3869 if (_ranges.empty()) {
3871 /* No selected time ranges: drag all points */
3872 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3873 uint32_t const N = i->line->npoints ();
3874 for (uint32_t j = 0; j < N; ++j) {
3875 i->points.push_back (i->line->nth (j));
3881 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3883 framecnt_t const half = (i->start + i->end) / 2;
3885 /* find the line that this audio range starts in */
3886 list<Line>::iterator j = _lines.begin();
3887 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3891 if (j != _lines.end()) {
3892 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3894 /* j is the line that this audio range starts in; fade into it;
3895 64 samples length plucked out of thin air.
3898 framepos_t a = i->start + 64;
3903 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3904 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3906 the_list->add (p, the_list->eval (p));
3907 j->line->add_always_in_view (p);
3908 the_list->add (q, the_list->eval (q));
3909 j->line->add_always_in_view (q);
3912 /* same thing for the end */
3915 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3919 if (j != _lines.end()) {
3920 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3922 /* j is the line that this audio range starts in; fade out of it;
3923 64 samples length plucked out of thin air.
3926 framepos_t b = i->end - 64;
3931 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3932 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
3934 the_list->add (p, the_list->eval (p));
3935 j->line->add_always_in_view (p);
3936 the_list->add (q, the_list->eval (q));
3937 j->line->add_always_in_view (q);
3941 _nothing_to_drag = true;
3943 /* Find all the points that should be dragged and put them in the relevant
3944 points lists in the Line structs.
3947 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3949 uint32_t const N = i->line->npoints ();
3950 for (uint32_t j = 0; j < N; ++j) {
3952 /* here's a control point on this line */
3953 ControlPoint* p = i->line->nth (j);
3954 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
3956 /* see if it's inside a range */
3957 list<AudioRange>::const_iterator k = _ranges.begin ();
3958 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
3962 if (k != _ranges.end()) {
3963 /* dragging this point */
3964 _nothing_to_drag = false;
3965 i->points.push_back (p);
3971 if (_nothing_to_drag) {
3975 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3976 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
3981 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
3983 if (_nothing_to_drag) {
3987 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3988 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
3990 /* we are ignoring x position for this drag, so we can just pass in anything */
3991 i->line->drag_motion (0, f, true, false);
3996 AutomationRangeDrag::finished (GdkEvent* event, bool)
3998 if (_nothing_to_drag) {
4002 motion (event, false);
4003 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4004 i->line->end_drag ();
4005 i->line->clear_always_in_view ();
4008 _editor->session()->commit_reversible_command ();
4012 AutomationRangeDrag::aborted ()
4014 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4015 i->line->clear_always_in_view ();
4020 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4023 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4024 layer = v->region()->layer ();
4025 initial_y = v->get_canvas_group()->property_y ();
4026 initial_playlist = v->region()->playlist ();
4027 initial_position = v->region()->position ();
4028 initial_end = v->region()->position () + v->region()->length ();