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 start */
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);
1556 _editor->show_verbose_time_cursor (region_end, 10);
1559 _editor->show_verbose_time_cursor (pf, 10);
1563 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1564 i->view->region()->suspend_property_changes ();
1569 TrimDrag::motion (GdkEvent* event, bool first_move)
1571 RegionView* rv = _primary;
1574 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1575 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1576 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1578 if (tv && tv->is_track()) {
1579 speed = tv->track()->speed();
1582 framecnt_t const dt = adjusted_current_frame (event) - grab_frame ();
1588 switch (_operation) {
1590 trim_type = "Region start trim";
1593 trim_type = "Region end trim";
1596 trim_type = "Region content trim";
1600 _editor->begin_reversible_command (trim_type);
1601 _have_transaction = true;
1603 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1604 RegionView* rv = i->view;
1605 rv->fake_set_opaque (false);
1606 rv->enable_display (false);
1607 rv->region()->clear_changes ();
1609 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1612 arv->temporarily_hide_envelope ();
1615 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1616 insert_result = _editor->motion_frozen_playlists.insert (pl);
1618 if (insert_result.second) {
1624 bool non_overlap_trim = false;
1626 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1627 non_overlap_trim = true;
1630 switch (_operation) {
1632 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1633 i->view->trim_start (i->initial_position + dt, non_overlap_trim);
1638 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1639 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1645 bool swap_direction = false;
1647 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1648 swap_direction = true;
1651 framecnt_t frame_delta = 0;
1653 bool left_direction = false;
1654 if (last_pointer_frame() > adjusted_current_frame(event)) {
1655 left_direction = true;
1658 if (left_direction) {
1659 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1661 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1664 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1665 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1671 switch (_operation) {
1673 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->position() / speed), 10);
1676 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->last_frame() / speed), 10);
1679 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1686 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1688 if (movement_occurred) {
1689 motion (event, false);
1691 if (!_editor->selection->selected (_primary)) {
1692 _primary->thaw_after_trim ();
1695 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1696 i->view->thaw_after_trim ();
1697 i->view->enable_display (true);
1698 i->view->fake_set_opaque (true);
1699 if (_have_transaction) {
1700 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1704 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1708 _editor->motion_frozen_playlists.clear ();
1710 if (_have_transaction) {
1711 _editor->commit_reversible_command();
1715 /* no mouse movement */
1716 _editor->point_trim (event, adjusted_current_frame (event));
1719 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1720 i->view->region()->resume_property_changes ();
1725 TrimDrag::aborted ()
1727 /* Our motion method is changing model state, so use the Undo system
1728 to cancel. Perhaps not ideal, as this will leave an Undo point
1729 behind which may be slightly odd from the user's point of view.
1734 if (_have_transaction) {
1738 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1739 i->view->region()->resume_property_changes ();
1743 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1747 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1749 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1754 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1757 // create a dummy marker for visual representation of moving the copy.
1758 // The actual copying is not done before we reach the finish callback.
1760 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1762 MeterMarker* new_marker = new MeterMarker (
1764 *_editor->meter_group,
1765 *_editor->cursor_group,
1766 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1768 *new MeterSection (_marker->meter())
1771 _item = &new_marker->the_item ();
1772 _marker = new_marker;
1776 MetricSection& section (_marker->meter());
1778 if (!section.movable()) {
1784 Drag::start_grab (event, cursor);
1786 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1788 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1792 MeterMarkerDrag::motion (GdkEvent* event, bool)
1794 framepos_t const pf = adjusted_current_frame (event);
1796 _marker->set_position (pf);
1798 _editor->show_verbose_time_cursor (pf, 10);
1802 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1804 if (!movement_occurred) {
1808 motion (event, false);
1812 TempoMap& map (_editor->session()->tempo_map());
1813 map.bbt_time (last_pointer_frame(), when);
1815 if (_copy == true) {
1816 _editor->begin_reversible_command (_("copy meter mark"));
1817 XMLNode &before = map.get_state();
1818 map.add_meter (_marker->meter(), when);
1819 XMLNode &after = map.get_state();
1820 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1821 _editor->commit_reversible_command ();
1823 // delete the dummy marker we used for visual representation of copying.
1824 // a new visual marker will show up automatically.
1827 _editor->begin_reversible_command (_("move meter mark"));
1828 XMLNode &before = map.get_state();
1829 map.move_meter (_marker->meter(), when);
1830 XMLNode &after = map.get_state();
1831 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1832 _editor->commit_reversible_command ();
1837 MeterMarkerDrag::aborted ()
1839 _marker->set_position (_marker->meter().frame ());
1842 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1846 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1848 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1853 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1857 // create a dummy marker for visual representation of moving the copy.
1858 // The actual copying is not done before we reach the finish callback.
1860 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1862 TempoMarker* new_marker = new TempoMarker (
1864 *_editor->tempo_group,
1865 *_editor->cursor_group,
1866 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1868 *new TempoSection (_marker->tempo())
1871 _item = &new_marker->the_item ();
1872 _marker = new_marker;
1876 Drag::start_grab (event, cursor);
1878 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1879 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1883 TempoMarkerDrag::motion (GdkEvent* event, bool)
1885 framepos_t const pf = adjusted_current_frame (event);
1886 _marker->set_position (pf);
1887 _editor->show_verbose_time_cursor (pf, 10);
1891 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1893 if (!movement_occurred) {
1897 motion (event, false);
1901 TempoMap& map (_editor->session()->tempo_map());
1902 map.bbt_time (last_pointer_frame(), when);
1904 if (_copy == true) {
1905 _editor->begin_reversible_command (_("copy tempo mark"));
1906 XMLNode &before = map.get_state();
1907 map.add_tempo (_marker->tempo(), when);
1908 XMLNode &after = map.get_state();
1909 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1910 _editor->commit_reversible_command ();
1912 // delete the dummy marker we used for visual representation of copying.
1913 // a new visual marker will show up automatically.
1916 _editor->begin_reversible_command (_("move tempo mark"));
1917 XMLNode &before = map.get_state();
1918 map.move_tempo (_marker->tempo(), when);
1919 XMLNode &after = map.get_state();
1920 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1921 _editor->commit_reversible_command ();
1926 TempoMarkerDrag::aborted ()
1928 _marker->set_position (_marker->tempo().frame());
1931 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1935 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
1937 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1942 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1944 Drag::start_grab (event, c);
1948 framepos_t where = _editor->event_frame (event, 0, 0);
1950 _editor->snap_to_with_modifier (where, event);
1951 _editor->playhead_cursor->set_position (where);
1955 if (_cursor == _editor->playhead_cursor) {
1956 _editor->_dragging_playhead = true;
1958 Session* s = _editor->session ();
1961 if (_was_rolling && _stop) {
1965 if (s->is_auditioning()) {
1966 s->cancel_audition ();
1969 s->request_suspend_timecode_transmission ();
1971 if (s->timecode_transmission_suspended ()) {
1972 framepos_t const f = _editor->playhead_cursor->current_frame;
1973 s->send_mmc_locate (f);
1974 s->send_full_time_code (f);
1979 _pointer_frame_offset = raw_grab_frame() - _cursor->current_frame;
1981 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1985 CursorDrag::motion (GdkEvent* event, bool)
1987 framepos_t const adjusted_frame = adjusted_current_frame (event);
1989 if (adjusted_frame == last_pointer_frame()) {
1993 _cursor->set_position (adjusted_frame);
1995 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1997 Session* s = _editor->session ();
1998 if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
1999 framepos_t const f = _editor->playhead_cursor->current_frame;
2000 s->send_mmc_locate (f);
2001 s->send_full_time_code (f);
2006 _editor->update_canvas_now ();
2008 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2012 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2014 _editor->_dragging_playhead = false;
2016 if (!movement_occurred && _stop) {
2020 motion (event, false);
2022 if (_item == &_editor->playhead_cursor->canvas_item) {
2023 Session* s = _editor->session ();
2025 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2026 _editor->_pending_locate_request = true;
2027 s->request_resume_timecode_transmission ();
2033 CursorDrag::aborted ()
2035 if (_editor->_dragging_playhead) {
2036 _editor->session()->request_resume_timecode_transmission ();
2037 _editor->_dragging_playhead = false;
2040 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2043 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2044 : RegionDrag (e, i, p, v)
2046 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2050 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2052 Drag::start_grab (event, cursor);
2054 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2055 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2057 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2058 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2060 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2064 FadeInDrag::motion (GdkEvent* event, bool)
2066 framecnt_t fade_length;
2068 framepos_t const pos = adjusted_current_frame (event);
2070 boost::shared_ptr<Region> region = _primary->region ();
2072 if (pos < (region->position() + 64)) {
2073 fade_length = 64; // this should be a minimum defined somewhere
2074 } else if (pos > region->last_frame()) {
2075 fade_length = region->length();
2077 fade_length = pos - region->position();
2080 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2082 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2088 tmp->reset_fade_in_shape_width (fade_length);
2089 tmp->show_fade_line((framecnt_t) fade_length);
2092 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2096 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2098 if (!movement_occurred) {
2102 framecnt_t fade_length;
2104 framepos_t const pos = adjusted_current_frame (event);
2106 boost::shared_ptr<Region> region = _primary->region ();
2108 if (pos < (region->position() + 64)) {
2109 fade_length = 64; // this should be a minimum defined somewhere
2110 } else if (pos > region->last_frame()) {
2111 fade_length = region->length();
2113 fade_length = pos - region->position();
2116 _editor->begin_reversible_command (_("change fade in length"));
2118 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2120 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2126 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2127 XMLNode &before = alist->get_state();
2129 tmp->audio_region()->set_fade_in_length (fade_length);
2130 tmp->audio_region()->set_fade_in_active (true);
2131 tmp->hide_fade_line();
2133 XMLNode &after = alist->get_state();
2134 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2137 _editor->commit_reversible_command ();
2141 FadeInDrag::aborted ()
2143 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2144 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2150 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2151 tmp->hide_fade_line();
2155 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2156 : RegionDrag (e, i, p, v)
2158 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2162 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2164 Drag::start_grab (event, cursor);
2166 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2167 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2169 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2170 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2172 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2176 FadeOutDrag::motion (GdkEvent* event, bool)
2178 framecnt_t fade_length;
2180 framepos_t const pos = adjusted_current_frame (event);
2182 boost::shared_ptr<Region> region = _primary->region ();
2184 if (pos > (region->last_frame() - 64)) {
2185 fade_length = 64; // this should really be a minimum fade defined somewhere
2187 else if (pos < region->position()) {
2188 fade_length = region->length();
2191 fade_length = region->last_frame() - pos;
2194 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2196 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2202 tmp->reset_fade_out_shape_width (fade_length);
2203 tmp->show_fade_line(region->length() - fade_length);
2206 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2210 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2212 if (!movement_occurred) {
2216 framecnt_t fade_length;
2218 framepos_t const pos = adjusted_current_frame (event);
2220 boost::shared_ptr<Region> region = _primary->region ();
2222 if (pos > (region->last_frame() - 64)) {
2223 fade_length = 64; // this should really be a minimum fade defined somewhere
2225 else if (pos < region->position()) {
2226 fade_length = region->length();
2229 fade_length = region->last_frame() - pos;
2232 _editor->begin_reversible_command (_("change fade out length"));
2234 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2236 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2242 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2243 XMLNode &before = alist->get_state();
2245 tmp->audio_region()->set_fade_out_length (fade_length);
2246 tmp->audio_region()->set_fade_out_active (true);
2247 tmp->hide_fade_line();
2249 XMLNode &after = alist->get_state();
2250 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2253 _editor->commit_reversible_command ();
2257 FadeOutDrag::aborted ()
2259 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2260 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2266 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2267 tmp->hide_fade_line();
2271 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2274 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2276 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2279 _points.push_back (Gnome::Art::Point (0, 0));
2280 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2282 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2283 _line->property_width_pixels() = 1;
2284 _line->property_points () = _points;
2287 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2290 MarkerDrag::~MarkerDrag ()
2292 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2298 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2300 Drag::start_grab (event, cursor);
2304 Location *location = _editor->find_location_from_marker (_marker, is_start);
2305 _editor->_dragging_edit_point = true;
2307 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2309 update_item (location);
2311 // _drag_line->show();
2312 // _line->raise_to_top();
2315 _editor->show_verbose_time_cursor (location->start(), 10);
2317 _editor->show_verbose_time_cursor (location->end(), 10);
2320 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2323 case Selection::Toggle:
2324 _editor->selection->toggle (_marker);
2326 case Selection::Set:
2327 if (!_editor->selection->selected (_marker)) {
2328 _editor->selection->set (_marker);
2331 case Selection::Extend:
2333 Locations::LocationList ll;
2334 list<Marker*> to_add;
2336 _editor->selection->markers.range (s, e);
2337 s = min (_marker->position(), s);
2338 e = max (_marker->position(), e);
2341 if (e < max_framepos) {
2344 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2345 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2346 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2349 to_add.push_back (lm->start);
2352 to_add.push_back (lm->end);
2356 if (!to_add.empty()) {
2357 _editor->selection->add (to_add);
2361 case Selection::Add:
2362 _editor->selection->add (_marker);
2366 /* Set up copies for us to manipulate during the drag */
2368 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2369 Location* l = _editor->find_location_from_marker (*i, is_start);
2370 _copied_locations.push_back (new Location (*l));
2375 MarkerDrag::motion (GdkEvent* event, bool)
2377 framecnt_t f_delta = 0;
2379 bool move_both = false;
2381 Location *real_location;
2382 Location *copy_location = 0;
2384 framepos_t const newframe = adjusted_current_frame (event);
2386 framepos_t next = newframe;
2388 if (newframe == last_pointer_frame()) {
2392 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2396 MarkerSelection::iterator i;
2397 list<Location*>::iterator x;
2399 /* find the marker we're dragging, and compute the delta */
2401 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2402 x != _copied_locations.end() && i != _editor->selection->markers.end();
2408 if (marker == _marker) {
2410 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2415 if (real_location->is_mark()) {
2416 f_delta = newframe - copy_location->start();
2420 switch (marker->type()) {
2421 case Marker::SessionStart:
2422 case Marker::RangeStart:
2423 case Marker::LoopStart:
2424 case Marker::PunchIn:
2425 f_delta = newframe - copy_location->start();
2428 case Marker::SessionEnd:
2429 case Marker::RangeEnd:
2430 case Marker::LoopEnd:
2431 case Marker::PunchOut:
2432 f_delta = newframe - copy_location->end();
2435 /* what kind of marker is this ? */
2443 if (i == _editor->selection->markers.end()) {
2444 /* hmm, impossible - we didn't find the dragged marker */
2448 /* now move them all */
2450 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2451 x != _copied_locations.end() && i != _editor->selection->markers.end();
2457 /* call this to find out if its the start or end */
2459 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2463 if (real_location->locked()) {
2467 if (copy_location->is_mark()) {
2471 copy_location->set_start (copy_location->start() + f_delta);
2475 framepos_t new_start = copy_location->start() + f_delta;
2476 framepos_t new_end = copy_location->end() + f_delta;
2478 if (is_start) { // start-of-range marker
2481 copy_location->set_start (new_start);
2482 copy_location->set_end (new_end);
2483 } else if (new_start < copy_location->end()) {
2484 copy_location->set_start (new_start);
2486 _editor->snap_to (next, 1, true);
2487 copy_location->set_end (next);
2488 copy_location->set_start (newframe);
2491 } else { // end marker
2494 copy_location->set_end (new_end);
2495 copy_location->set_start (new_start);
2496 } else if (new_end > copy_location->start()) {
2497 copy_location->set_end (new_end);
2498 } else if (newframe > 0) {
2499 _editor->snap_to (next, -1, true);
2500 copy_location->set_start (next);
2501 copy_location->set_end (newframe);
2506 update_item (copy_location);
2508 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2511 lm->set_position (copy_location->start(), copy_location->end());
2515 assert (!_copied_locations.empty());
2517 _editor->show_verbose_time_cursor (newframe, 10);
2520 _editor->update_canvas_now ();
2525 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2527 if (!movement_occurred) {
2529 /* just a click, do nothing but finish
2530 off the selection process
2533 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2536 case Selection::Set:
2537 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2538 _editor->selection->set (_marker);
2542 case Selection::Toggle:
2543 case Selection::Extend:
2544 case Selection::Add:
2551 _editor->_dragging_edit_point = false;
2553 _editor->begin_reversible_command ( _("move marker") );
2554 XMLNode &before = _editor->session()->locations()->get_state();
2556 MarkerSelection::iterator i;
2557 list<Location*>::iterator x;
2560 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2561 x != _copied_locations.end() && i != _editor->selection->markers.end();
2564 Location * location = _editor->find_location_from_marker (*i, is_start);
2568 if (location->locked()) {
2572 if (location->is_mark()) {
2573 location->set_start ((*x)->start());
2575 location->set ((*x)->start(), (*x)->end());
2580 XMLNode &after = _editor->session()->locations()->get_state();
2581 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2582 _editor->commit_reversible_command ();
2588 MarkerDrag::aborted ()
2594 MarkerDrag::update_item (Location* location)
2596 double const x1 = _editor->frame_to_pixel (location->start());
2598 _points.front().set_x(x1);
2599 _points.back().set_x(x1);
2600 _line->property_points() = _points;
2603 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2605 _cumulative_x_drag (0),
2606 _cumulative_y_drag (0)
2608 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2610 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2616 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2618 Drag::start_grab (event, _editor->cursors()->fader);
2620 // start the grab at the center of the control point so
2621 // the point doesn't 'jump' to the mouse after the first drag
2622 _fixed_grab_x = _point->get_x();
2623 _fixed_grab_y = _point->get_y();
2625 float const fraction = 1 - (_point->get_y() / _point->line().height());
2627 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2629 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2630 event->button.x + 10, event->button.y + 10);
2632 _editor->show_verbose_canvas_cursor ();
2636 ControlPointDrag::motion (GdkEvent* event, bool)
2638 double dx = _drags->current_pointer_x() - last_pointer_x();
2639 double dy = _drags->current_pointer_y() - last_pointer_y();
2641 if (event->button.state & Keyboard::SecondaryModifier) {
2646 /* coordinate in pixels relative to the start of the region (for region-based automation)
2647 or track (for track-based automation) */
2648 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2649 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2651 // calculate zero crossing point. back off by .01 to stay on the
2652 // positive side of zero
2653 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2655 // make sure we hit zero when passing through
2656 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2660 if (_x_constrained) {
2663 if (_y_constrained) {
2667 _cumulative_x_drag = cx - _fixed_grab_x;
2668 _cumulative_y_drag = cy - _fixed_grab_y;
2672 cy = min ((double) _point->line().height(), cy);
2674 framepos_t cx_frames = _editor->unit_to_frame (cx);
2676 if (!_x_constrained) {
2677 _editor->snap_to_with_modifier (cx_frames, event);
2680 cx_frames = min (cx_frames, _point->line().maximum_time());
2682 float const fraction = 1.0 - (cy / _point->line().height());
2684 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2686 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2688 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2692 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2694 if (!movement_occurred) {
2698 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2699 _editor->reset_point_selection ();
2703 motion (event, false);
2706 _point->line().end_drag ();
2707 _editor->session()->commit_reversible_command ();
2711 ControlPointDrag::aborted ()
2713 _point->line().reset ();
2717 ControlPointDrag::active (Editing::MouseMode m)
2719 if (m == Editing::MouseGain) {
2720 /* always active in mouse gain */
2724 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2725 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2728 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2731 _cumulative_y_drag (0)
2733 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2737 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2739 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2742 _item = &_line->grab_item ();
2744 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2745 origin, and ditto for y.
2748 double cx = event->button.x;
2749 double cy = event->button.y;
2751 _line->parent_group().w2i (cx, cy);
2753 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2758 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2759 /* no adjacent points */
2763 Drag::start_grab (event, _editor->cursors()->fader);
2765 /* store grab start in parent frame */
2770 double fraction = 1.0 - (cy / _line->height());
2772 _line->start_drag_line (before, after, fraction);
2774 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2775 event->button.x + 10, event->button.y + 10);
2777 _editor->show_verbose_canvas_cursor ();
2781 LineDrag::motion (GdkEvent* event, bool)
2783 double dy = _drags->current_pointer_y() - last_pointer_y();
2785 if (event->button.state & Keyboard::SecondaryModifier) {
2789 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2791 _cumulative_y_drag = cy - _fixed_grab_y;
2794 cy = min ((double) _line->height(), cy);
2796 double const fraction = 1.0 - (cy / _line->height());
2800 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2806 /* we are ignoring x position for this drag, so we can just pass in anything */
2807 _line->drag_motion (0, fraction, true, push);
2809 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2813 LineDrag::finished (GdkEvent* event, bool)
2815 motion (event, false);
2817 _editor->session()->commit_reversible_command ();
2821 LineDrag::aborted ()
2826 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2829 _cumulative_x_drag (0)
2831 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2835 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2837 Drag::start_grab (event);
2839 _line = reinterpret_cast<SimpleLine*> (_item);
2842 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2844 double cx = event->button.x;
2845 double cy = event->button.y;
2847 _item->property_parent().get_value()->w2i(cx, cy);
2849 /* store grab start in parent frame */
2850 _region_view_grab_x = cx;
2852 _before = _line->property_x1();
2854 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2856 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2860 FeatureLineDrag::motion (GdkEvent*, bool)
2862 double dx = _drags->current_pointer_x() - last_pointer_x();
2864 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2866 _cumulative_x_drag += dx;
2868 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2877 _line->property_x1() = cx;
2878 _line->property_x2() = cx;
2880 _before = _line->property_x1();
2884 FeatureLineDrag::finished (GdkEvent*, bool)
2886 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2887 _arv->update_transient(_before, _line->property_x1());
2891 FeatureLineDrag::aborted ()
2896 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2899 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2903 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2905 Drag::start_grab (event);
2906 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2910 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2917 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2919 framepos_t grab = grab_frame ();
2920 if (Config->get_rubberbanding_snaps_to_grid ()) {
2921 _editor->snap_to_with_modifier (grab, event);
2924 /* base start and end on initial click position */
2934 if (_drags->current_pointer_y() < grab_y()) {
2935 y1 = _drags->current_pointer_y();
2938 y2 = _drags->current_pointer_y();
2943 if (start != end || y1 != y2) {
2945 double x1 = _editor->frame_to_pixel (start);
2946 double x2 = _editor->frame_to_pixel (end);
2948 _editor->rubberband_rect->property_x1() = x1;
2949 _editor->rubberband_rect->property_y1() = y1;
2950 _editor->rubberband_rect->property_x2() = x2;
2951 _editor->rubberband_rect->property_y2() = y2;
2953 _editor->rubberband_rect->show();
2954 _editor->rubberband_rect->raise_to_top();
2956 _editor->show_verbose_time_cursor (pf, 10);
2961 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2963 if (movement_occurred) {
2965 motion (event, false);
2968 if (_drags->current_pointer_y() < grab_y()) {
2969 y1 = _drags->current_pointer_y();
2972 y2 = _drags->current_pointer_y();
2977 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2980 _editor->begin_reversible_command (_("rubberband selection"));
2982 if (grab_frame() < last_pointer_frame()) {
2983 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
2985 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
2989 _editor->commit_reversible_command ();
2993 if (!getenv("ARDOUR_SAE")) {
2994 _editor->selection->clear_tracks();
2996 _editor->selection->clear_regions();
2997 _editor->selection->clear_points ();
2998 _editor->selection->clear_lines ();
3001 _editor->rubberband_rect->hide();
3005 RubberbandSelectDrag::aborted ()
3007 _editor->rubberband_rect->hide ();
3010 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3011 : RegionDrag (e, i, p, v)
3013 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3017 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3019 Drag::start_grab (event, cursor);
3021 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3025 TimeFXDrag::motion (GdkEvent* event, bool)
3027 RegionView* rv = _primary;
3029 framepos_t const pf = adjusted_current_frame (event);
3031 if (pf > rv->region()->position()) {
3032 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3035 _editor->show_verbose_time_cursor (pf, 10);
3039 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3041 _primary->get_time_axis_view().hide_timestretch ();
3043 if (!movement_occurred) {
3047 if (last_pointer_frame() < _primary->region()->position()) {
3048 /* backwards drag of the left edge - not usable */
3052 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3054 float percentage = (double) newlen / (double) _primary->region()->length();
3056 #ifndef USE_RUBBERBAND
3057 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3058 if (_primary->region()->data_type() == DataType::AUDIO) {
3059 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3063 _editor->begin_reversible_command (_("timestretch"));
3065 // XXX how do timeFX on multiple regions ?
3070 if (_editor->time_stretch (rs, percentage) == -1) {
3071 error << _("An error occurred while executing time stretch operation") << endmsg;
3076 TimeFXDrag::aborted ()
3078 _primary->get_time_axis_view().hide_timestretch ();
3081 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3084 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3088 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3090 Drag::start_grab (event);
3094 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3096 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3100 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3102 if (movement_occurred && _editor->session()) {
3103 /* make sure we stop */
3104 _editor->session()->request_transport_speed (0.0);
3109 ScrubDrag::aborted ()
3114 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3118 , _original_pointer_time_axis (-1)
3119 , _last_pointer_time_axis (-1)
3121 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3125 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3127 framepos_t start = 0;
3130 if (_editor->session() == 0) {
3134 Gdk::Cursor* cursor = 0;
3136 switch (_operation) {
3137 case CreateSelection:
3138 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3143 cursor = _editor->cursors()->selector;
3144 Drag::start_grab (event, cursor);
3147 case SelectionStartTrim:
3148 if (_editor->clicked_axisview) {
3149 _editor->clicked_axisview->order_selection_trims (_item, true);
3151 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3152 start = _editor->selection->time[_editor->clicked_selection].start;
3153 _pointer_frame_offset = raw_grab_frame() - start;
3156 case SelectionEndTrim:
3157 if (_editor->clicked_axisview) {
3158 _editor->clicked_axisview->order_selection_trims (_item, false);
3160 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3161 end = _editor->selection->time[_editor->clicked_selection].end;
3162 _pointer_frame_offset = raw_grab_frame() - end;
3166 start = _editor->selection->time[_editor->clicked_selection].start;
3167 Drag::start_grab (event, cursor);
3168 _pointer_frame_offset = raw_grab_frame() - start;
3172 if (_operation == SelectionMove) {
3173 _editor->show_verbose_time_cursor (start, 10);
3175 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3178 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3182 SelectionDrag::motion (GdkEvent* event, bool first_move)
3184 framepos_t start = 0;
3188 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3189 if (pending_time_axis.first == 0) {
3193 framepos_t const pending_position = adjusted_current_frame (event);
3195 /* only alter selection if things have changed */
3197 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3201 switch (_operation) {
3202 case CreateSelection:
3204 framepos_t grab = grab_frame ();
3207 _editor->snap_to (grab);
3210 if (pending_position < grab_frame()) {
3211 start = pending_position;
3214 end = pending_position;
3218 /* first drag: Either add to the selection
3219 or create a new selection
3225 /* adding to the selection */
3226 _editor->set_selected_track_as_side_effect (Selection::Add);
3227 //_editor->selection->add (_editor->clicked_axisview);
3228 _editor->clicked_selection = _editor->selection->add (start, end);
3233 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3234 //_editor->selection->set (_editor->clicked_axisview);
3235 _editor->set_selected_track_as_side_effect (Selection::Set);
3238 _editor->clicked_selection = _editor->selection->set (start, end);
3242 /* select the track that we're in */
3243 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3244 // _editor->set_selected_track_as_side_effect (Selection::Add);
3245 _editor->selection->add (pending_time_axis.first);
3246 _added_time_axes.push_back (pending_time_axis.first);
3249 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3250 tracks that we selected in the first place.
3253 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3254 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3256 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3257 while (i != _added_time_axes.end()) {
3259 list<TimeAxisView*>::iterator tmp = i;
3262 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3263 _editor->selection->remove (*i);
3264 _added_time_axes.remove (*i);
3273 case SelectionStartTrim:
3275 start = _editor->selection->time[_editor->clicked_selection].start;
3276 end = _editor->selection->time[_editor->clicked_selection].end;
3278 if (pending_position > end) {
3281 start = pending_position;
3285 case SelectionEndTrim:
3287 start = _editor->selection->time[_editor->clicked_selection].start;
3288 end = _editor->selection->time[_editor->clicked_selection].end;
3290 if (pending_position < start) {
3293 end = pending_position;
3300 start = _editor->selection->time[_editor->clicked_selection].start;
3301 end = _editor->selection->time[_editor->clicked_selection].end;
3303 length = end - start;
3305 start = pending_position;
3306 _editor->snap_to (start);
3308 end = start + length;
3313 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3314 _editor->start_canvas_autoscroll (1, 0);
3318 _editor->selection->replace (_editor->clicked_selection, start, end);
3321 if (_operation == SelectionMove) {
3322 _editor->show_verbose_time_cursor(start, 10);
3324 _editor->show_verbose_time_cursor(pending_position, 10);
3329 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3331 Session* s = _editor->session();
3333 if (movement_occurred) {
3334 motion (event, false);
3335 /* XXX this is not object-oriented programming at all. ick */
3336 if (_editor->selection->time.consolidate()) {
3337 _editor->selection->TimeChanged ();
3340 /* XXX what if its a music time selection? */
3341 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3342 s->request_play_range (&_editor->selection->time, true);
3347 /* just a click, no pointer movement.*/
3349 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3350 _editor->selection->clear_time();
3353 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3354 _editor->selection->set (_editor->clicked_axisview);
3357 if (s && s->get_play_range () && s->transport_rolling()) {
3358 s->request_stop (false, false);
3363 _editor->stop_canvas_autoscroll ();
3367 SelectionDrag::aborted ()
3372 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3377 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3379 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3380 physical_screen_height (_editor->get_window()));
3381 _drag_rect->hide ();
3383 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3384 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3388 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3390 if (_editor->session() == 0) {
3394 Gdk::Cursor* cursor = 0;
3396 if (!_editor->temp_location) {
3397 _editor->temp_location = new Location (*_editor->session());
3400 switch (_operation) {
3401 case CreateRangeMarker:
3402 case CreateTransportMarker:
3403 case CreateCDMarker:
3405 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3410 cursor = _editor->cursors()->selector;
3414 Drag::start_grab (event, cursor);
3416 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3420 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3422 framepos_t start = 0;
3424 ArdourCanvas::SimpleRect *crect;
3426 switch (_operation) {
3427 case CreateRangeMarker:
3428 crect = _editor->range_bar_drag_rect;
3430 case CreateTransportMarker:
3431 crect = _editor->transport_bar_drag_rect;
3433 case CreateCDMarker:
3434 crect = _editor->cd_marker_bar_drag_rect;
3437 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3442 framepos_t const pf = adjusted_current_frame (event);
3444 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3445 framepos_t grab = grab_frame ();
3446 _editor->snap_to (grab);
3448 if (pf < grab_frame()) {
3456 /* first drag: Either add to the selection
3457 or create a new selection.
3462 _editor->temp_location->set (start, end);
3466 update_item (_editor->temp_location);
3468 //_drag_rect->raise_to_top();
3473 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3474 _editor->start_canvas_autoscroll (1, 0);
3478 _editor->temp_location->set (start, end);
3480 double x1 = _editor->frame_to_pixel (start);
3481 double x2 = _editor->frame_to_pixel (end);
3482 crect->property_x1() = x1;
3483 crect->property_x2() = x2;
3485 update_item (_editor->temp_location);
3488 _editor->show_verbose_time_cursor (pf, 10);
3493 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3495 Location * newloc = 0;
3499 if (movement_occurred) {
3500 motion (event, false);
3503 switch (_operation) {
3504 case CreateRangeMarker:
3505 case CreateCDMarker:
3507 _editor->begin_reversible_command (_("new range marker"));
3508 XMLNode &before = _editor->session()->locations()->get_state();
3509 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3510 if (_operation == CreateCDMarker) {
3511 flags = Location::IsRangeMarker | Location::IsCDMarker;
3512 _editor->cd_marker_bar_drag_rect->hide();
3515 flags = Location::IsRangeMarker;
3516 _editor->range_bar_drag_rect->hide();
3518 newloc = new Location (
3519 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3522 _editor->session()->locations()->add (newloc, true);
3523 XMLNode &after = _editor->session()->locations()->get_state();
3524 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3525 _editor->commit_reversible_command ();
3529 case CreateTransportMarker:
3530 // popup menu to pick loop or punch
3531 _editor->new_transport_marker_context_menu (&event->button, _item);
3535 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3537 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3542 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3544 if (end == max_framepos) {
3545 end = _editor->session()->current_end_frame ();
3548 if (start == max_framepos) {
3549 start = _editor->session()->current_start_frame ();
3552 switch (_editor->mouse_mode) {
3554 /* find the two markers on either side and then make the selection from it */
3555 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3559 /* find the two markers on either side of the click and make the range out of it */
3560 _editor->selection->set (start, end);
3569 _editor->stop_canvas_autoscroll ();
3573 RangeMarkerBarDrag::aborted ()
3579 RangeMarkerBarDrag::update_item (Location* location)
3581 double const x1 = _editor->frame_to_pixel (location->start());
3582 double const x2 = _editor->frame_to_pixel (location->end());
3584 _drag_rect->property_x1() = x1;
3585 _drag_rect->property_x2() = x2;
3588 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3592 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3596 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3598 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3599 Drag::start_grab (event, _editor->cursors()->zoom_out);
3602 Drag::start_grab (event, _editor->cursors()->zoom_in);
3606 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3610 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3615 framepos_t const pf = adjusted_current_frame (event);
3617 framepos_t grab = grab_frame ();
3618 _editor->snap_to_with_modifier (grab, event);
3620 /* base start and end on initial click position */
3632 _editor->zoom_rect->show();
3633 _editor->zoom_rect->raise_to_top();
3636 _editor->reposition_zoom_rect(start, end);
3638 _editor->show_verbose_time_cursor (pf, 10);
3643 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3645 if (movement_occurred) {
3646 motion (event, false);
3648 if (grab_frame() < last_pointer_frame()) {
3649 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3651 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3654 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3657 _editor->zoom_rect->hide();
3661 MouseZoomDrag::aborted ()
3663 _editor->zoom_rect->hide ();
3666 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3668 , _cumulative_dx (0)
3669 , _cumulative_dy (0)
3671 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3673 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3674 _region = &_primary->region_view ();
3675 _note_height = _region->midi_stream_view()->note_height ();
3679 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3681 Drag::start_grab (event);
3683 if (!(_was_selected = _primary->selected())) {
3685 /* tertiary-click means extend selection - we'll do that on button release,
3686 so don't add it here, because otherwise we make it hard to figure
3687 out the "extend-to" range.
3690 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3693 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3696 _region->note_selected (_primary, true);
3698 _region->unique_select (_primary);
3704 /** @return Current total drag x change in frames */
3706 NoteDrag::total_dx () const
3709 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3711 /* primary note time */
3712 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3714 /* new time of the primary note relative to the region position */
3715 frameoffset_t const st = n + dx;
3717 /* snap and return corresponding delta */
3718 return _region->snap_frame_to_frame (st) - n;
3721 /** @return Current total drag y change in notes */
3723 NoteDrag::total_dy () const
3725 /* this is `backwards' to make increasing note number go in the right direction */
3726 double const dy = _drags->current_pointer_y() - grab_y();
3731 if (abs (dy) >= _note_height) {
3733 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3735 ndy = (int8_t) floor (dy / _note_height / 2.0);
3739 /* more positive value = higher pitch and higher y-axis position on track,
3740 which is the inverse of the X-centric geometric universe
3747 NoteDrag::motion (GdkEvent *, bool)
3749 /* Total change in x and y since the start of the drag */
3750 frameoffset_t const dx = total_dx ();
3751 int8_t const dy = -total_dy ();
3753 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3754 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3755 double const tdy = dy * _note_height - _cumulative_dy;
3758 _cumulative_dx += tdx;
3759 _cumulative_dy += tdy;
3761 int8_t note_delta = total_dy();
3763 _region->move_selection (tdx, tdy, note_delta);
3766 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3767 (int) floor (_primary->note()->note() + note_delta));
3769 _editor->show_verbose_canvas_cursor_with (buf);
3774 NoteDrag::finished (GdkEvent* ev, bool moved)
3777 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3779 if (_was_selected) {
3780 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3782 _region->note_deselected (_primary);
3785 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3786 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3788 if (!extend && !add && _region->selection_size() > 1) {
3789 _region->unique_select (_primary);
3790 } else if (extend) {
3791 _region->note_selected (_primary, true, true);
3793 /* it was added during button press */
3798 _region->note_dropped (_primary, total_dx(), total_dy());
3803 NoteDrag::aborted ()
3808 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3809 : Drag (editor, item)
3811 , _nothing_to_drag (false)
3813 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3815 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3818 /* get all lines in the automation view */
3819 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3821 /* find those that overlap the ranges being dragged */
3822 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3823 while (i != lines.end ()) {
3824 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3827 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3829 /* check this range against all the AudioRanges that we are using */
3830 list<AudioRange>::const_iterator k = _ranges.begin ();
3831 while (k != _ranges.end()) {
3832 if (k->coverage (r.first, r.second) != OverlapNone) {
3838 /* add it to our list if it overlaps at all */
3839 if (k != _ranges.end()) {
3844 _lines.push_back (n);
3850 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3854 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3856 Drag::start_grab (event, cursor);
3858 /* Get line states before we start changing things */
3859 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3860 i->state = &i->line->get_state ();
3863 if (_ranges.empty()) {
3865 /* No selected time ranges: drag all points */
3866 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3867 uint32_t const N = i->line->npoints ();
3868 for (uint32_t j = 0; j < N; ++j) {
3869 i->points.push_back (i->line->nth (j));
3875 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3877 framecnt_t const half = (i->start + i->end) / 2;
3879 /* find the line that this audio range starts in */
3880 list<Line>::iterator j = _lines.begin();
3881 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3885 if (j != _lines.end()) {
3886 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3888 /* j is the line that this audio range starts in; fade into it;
3889 64 samples length plucked out of thin air.
3892 framepos_t a = i->start + 64;
3897 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3898 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3900 the_list->add (p, the_list->eval (p));
3901 j->line->add_always_in_view (p);
3902 the_list->add (q, the_list->eval (q));
3903 j->line->add_always_in_view (q);
3906 /* same thing for the end */
3909 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3913 if (j != _lines.end()) {
3914 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3916 /* j is the line that this audio range starts in; fade out of it;
3917 64 samples length plucked out of thin air.
3920 framepos_t b = i->end - 64;
3925 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3926 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
3928 the_list->add (p, the_list->eval (p));
3929 j->line->add_always_in_view (p);
3930 the_list->add (q, the_list->eval (q));
3931 j->line->add_always_in_view (q);
3935 _nothing_to_drag = true;
3937 /* Find all the points that should be dragged and put them in the relevant
3938 points lists in the Line structs.
3941 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3943 uint32_t const N = i->line->npoints ();
3944 for (uint32_t j = 0; j < N; ++j) {
3946 /* here's a control point on this line */
3947 ControlPoint* p = i->line->nth (j);
3948 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
3950 /* see if it's inside a range */
3951 list<AudioRange>::const_iterator k = _ranges.begin ();
3952 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
3956 if (k != _ranges.end()) {
3957 /* dragging this point */
3958 _nothing_to_drag = false;
3959 i->points.push_back (p);
3965 if (_nothing_to_drag) {
3969 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3970 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
3975 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
3977 if (_nothing_to_drag) {
3981 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3982 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
3984 /* we are ignoring x position for this drag, so we can just pass in anything */
3985 i->line->drag_motion (0, f, true, false);
3990 AutomationRangeDrag::finished (GdkEvent* event, bool)
3992 if (_nothing_to_drag) {
3996 motion (event, false);
3997 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3998 i->line->end_drag ();
3999 i->line->clear_always_in_view ();
4002 _editor->session()->commit_reversible_command ();
4006 AutomationRangeDrag::aborted ()
4008 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4009 i->line->clear_always_in_view ();
4014 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4017 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4018 layer = v->region()->layer ();
4019 initial_y = v->get_canvas_group()->property_y ();
4020 initial_playlist = v->region()->playlist ();
4021 initial_position = v->region()->position ();
4022 initial_end = v->region()->position () + v->region()->length ();