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) {
94 _editor->set_follow_playhead (_old_follow_playhead);
100 DragManager::add (Drag* d)
102 d->set_manager (this);
103 _drags.push_back (d);
107 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
109 d->set_manager (this);
110 _drags.push_back (d);
115 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
117 /* Prevent follow playhead during the drag to be nice to the user */
118 _old_follow_playhead = _editor->follow_playhead ();
119 _editor->set_follow_playhead (false);
121 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
123 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
124 (*i)->start_grab (e, c);
128 /** Call end_grab for each active drag.
129 * @return true if any drag reported movement having occurred.
132 DragManager::end_grab (GdkEvent* e)
137 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
138 bool const t = (*i)->end_grab (e);
149 _editor->set_follow_playhead (_old_follow_playhead, false);
155 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
159 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
161 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
162 bool const t = (*i)->motion_handler (e, from_autoscroll);
173 DragManager::have_item (ArdourCanvas::Item* i) const
175 list<Drag*>::const_iterator j = _drags.begin ();
176 while (j != _drags.end() && (*j)->item () != i) {
180 return j != _drags.end ();
183 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
186 , _pointer_frame_offset (0)
187 , _move_threshold_passed (false)
188 , _raw_grab_frame (0)
190 , _last_pointer_frame (0)
196 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
202 cursor = _editor->which_grabber_cursor ();
205 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
209 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
212 cursor = _editor->which_grabber_cursor ();
215 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
217 if (Keyboard::is_button2_event (&event->button)) {
218 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
219 _y_constrained = true;
220 _x_constrained = false;
222 _y_constrained = false;
223 _x_constrained = true;
226 _x_constrained = false;
227 _y_constrained = false;
230 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
231 setup_pointer_frame_offset ();
232 _grab_frame = adjusted_frame (_raw_grab_frame, event);
233 _last_pointer_frame = _grab_frame;
234 _last_pointer_x = _grab_x;
235 _last_pointer_y = _grab_y;
237 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
241 if (_editor->session() && _editor->session()->transport_rolling()) {
244 _was_rolling = false;
247 switch (_editor->snap_type()) {
248 case SnapToRegionStart:
249 case SnapToRegionEnd:
250 case SnapToRegionSync:
251 case SnapToRegionBoundary:
252 _editor->build_region_boundary_cache ();
259 /** Call to end a drag `successfully'. Ungrabs item and calls
260 * subclass' finished() method.
262 * @param event GDK event, or 0.
263 * @return true if some movement occurred, otherwise false.
266 Drag::end_grab (GdkEvent* event)
268 _editor->stop_canvas_autoscroll ();
270 _item->ungrab (event ? event->button.time : 0);
272 finished (event, _move_threshold_passed);
274 _editor->hide_verbose_canvas_cursor();
276 return _move_threshold_passed;
280 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
284 if (f > _pointer_frame_offset) {
285 pos = f - _pointer_frame_offset;
289 _editor->snap_to_with_modifier (pos, event);
296 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
298 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
302 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
304 /* check to see if we have moved in any way that matters since the last motion event */
305 if (_move_threshold_passed &&
306 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
307 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
311 pair<framecnt_t, int> const threshold = move_threshold ();
313 bool const old_move_threshold_passed = _move_threshold_passed;
315 if (!from_autoscroll && !_move_threshold_passed) {
317 bool const xp = (::llabs (_drags->current_pointer_frame () - _grab_frame) >= threshold.first);
318 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
320 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
323 if (active (_editor->mouse_mode) && _move_threshold_passed) {
325 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
326 if (!from_autoscroll) {
327 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
330 motion (event, _move_threshold_passed != old_move_threshold_passed);
332 _last_pointer_x = _drags->current_pointer_x ();
333 _last_pointer_y = _drags->current_pointer_y ();
334 _last_pointer_frame = adjusted_current_frame (event);
342 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
350 aborted (_move_threshold_passed);
352 _editor->stop_canvas_autoscroll ();
353 _editor->hide_verbose_canvas_cursor ();
356 struct EditorOrderTimeAxisViewSorter {
357 bool operator() (TimeAxisView* a, TimeAxisView* b) {
358 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
359 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
361 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
365 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
369 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
371 /* Make a list of non-hidden tracks to refer to during the drag */
373 TrackViewList track_views = _editor->track_views;
374 track_views.sort (EditorOrderTimeAxisViewSorter ());
376 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
377 if (!(*i)->hidden()) {
379 _time_axis_views.push_back (*i);
381 TimeAxisView::Children children_list = (*i)->get_child_list ();
382 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
383 _time_axis_views.push_back (j->get());
388 /* the list of views can be empty at this point if this is a region list-insert drag
391 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
392 _views.push_back (DraggingView (*i, this));
395 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
399 RegionDrag::region_going_away (RegionView* v)
401 list<DraggingView>::iterator i = _views.begin ();
402 while (i != _views.end() && i->view != v) {
406 if (i != _views.end()) {
411 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
413 RegionDrag::find_time_axis_view (TimeAxisView* t) const
416 int const N = _time_axis_views.size ();
417 while (i < N && _time_axis_views[i] != t) {
428 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
429 : RegionDrag (e, i, p, v),
438 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
440 Drag::start_grab (event, cursor);
442 _editor->show_verbose_time_cursor (_last_frame_position, 10);
444 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
445 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
446 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
450 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
452 /* compute the amount of pointer motion in frames, and where
453 the region would be if we moved it by that much.
455 *pending_region_position = adjusted_current_frame (event);
457 framepos_t sync_frame;
458 framecnt_t sync_offset;
461 sync_offset = _primary->region()->sync_offset (sync_dir);
463 /* we don't handle a sync point that lies before zero.
465 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
467 sync_frame = *pending_region_position + (sync_dir*sync_offset);
469 _editor->snap_to_with_modifier (sync_frame, event);
471 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
474 *pending_region_position = _last_frame_position;
477 if (*pending_region_position > max_framepos - _primary->region()->length()) {
478 *pending_region_position = _last_frame_position;
483 /* in locked edit mode, reverse the usual meaning of _x_constrained */
484 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
486 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
488 /* x movement since last time */
489 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
491 /* total x movement */
492 framecnt_t total_dx = *pending_region_position;
493 if (regions_came_from_canvas()) {
494 total_dx = total_dx - grab_frame () + _pointer_frame_offset;
497 /* check that no regions have gone off the start of the session */
498 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
499 if ((i->view->region()->position() + total_dx) < 0) {
501 *pending_region_position = _last_frame_position;
506 _last_frame_position = *pending_region_position;
513 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
515 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
516 int const n = i->time_axis_view + delta_track;
517 if (n < 0 || n >= int (_time_axis_views.size())) {
518 /* off the top or bottom track */
522 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
523 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
524 /* not a track, or the wrong type */
528 int const l = i->layer + delta_layer;
529 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
530 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
531 If it has, the layers will be munged later anyway, so it's ok.
537 /* all regions being dragged are ok with this change */
542 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
544 assert (!_views.empty ());
546 /* Find the TimeAxisView that the pointer is now over */
547 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
549 /* Bail early if we're not over a track */
550 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
551 if (!rtv || !rtv->is_track()) {
552 _editor->hide_verbose_canvas_cursor ();
556 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
558 /* Here's the current pointer position in terms of time axis view and layer */
559 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
560 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
562 /* Work out the change in x */
563 framepos_t pending_region_position;
564 double const x_delta = compute_x_delta (event, &pending_region_position);
566 /* Work out the change in y */
567 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
568 int delta_layer = current_pointer_layer - _last_pointer_layer;
570 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
571 /* this y movement is not allowed, so do no y movement this time */
572 delta_time_axis_view = 0;
576 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
577 /* haven't reached next snap point, and we're not switching
578 trackviews nor layers. nothing to do.
583 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
585 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
587 RegionView* rv = i->view;
589 if (rv->region()->locked()) {
595 /* here we are calculating the y distance from the
596 top of the first track view to the top of the region
597 area of the track view that we're working on */
599 /* this x value is just a dummy value so that we have something
604 /* distance from the top of this track view to the region area
605 of our track view is always 1 */
609 /* convert to world coordinates, ie distance from the top of
612 rv->get_canvas_frame()->i2w (ix1, iy1);
614 /* compensate for the ruler section and the vertical scrollbar position */
615 iy1 += _editor->get_trackview_group_vertical_offset ();
617 // hide any dependent views
619 rv->get_time_axis_view().hide_dependent_views (*rv);
622 reparent to a non scrolling group so that we can keep the
623 region selection above all time axis views.
624 reparenting means we have to move the rv as the two
625 parent groups have different coordinates.
628 rv->get_canvas_group()->property_y() = iy1 - 1;
629 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
631 rv->fake_set_opaque (true);
634 /* Work out the change in y position of this region view */
638 /* If we have moved tracks, we'll fudge the layer delta so that the
639 region gets moved back onto layer 0 on its new track; this avoids
640 confusion when dragging regions from non-zero layers onto different
643 int this_delta_layer = delta_layer;
644 if (delta_time_axis_view != 0) {
645 this_delta_layer = - i->layer;
648 /* Move this region to layer 0 on its old track */
649 StreamView* lv = _time_axis_views[i->time_axis_view]->view ();
650 if (lv->layer_display() == Stacked) {
651 y_delta -= (lv->layers() - i->layer - 1) * lv->child_height ();
654 /* Now move it to its right layer on the current track */
655 StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view ();
656 if (cv->layer_display() == Stacked) {
657 y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height ();
661 if (delta_time_axis_view > 0) {
662 for (int j = 0; j < delta_time_axis_view; ++j) {
663 y_delta += _time_axis_views[i->time_axis_view + j]->current_height ();
666 /* start by subtracting the height of the track above where we are now */
667 for (int j = 1; j <= -delta_time_axis_view; ++j) {
668 y_delta -= _time_axis_views[i->time_axis_view - j]->current_height ();
673 rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ());
675 /* Update the DraggingView */
676 i->time_axis_view += delta_time_axis_view;
677 i->layer += this_delta_layer;
680 _editor->mouse_brush_insert_region (rv, pending_region_position);
682 rv->move (x_delta, y_delta);
685 } /* foreach region */
687 _total_x_delta += x_delta;
690 _editor->cursor_group->raise_to_top();
693 if (x_delta != 0 && !_brushing) {
694 _editor->show_verbose_time_cursor (_last_frame_position, 10);
697 _last_pointer_time_axis_view += delta_time_axis_view;
698 _last_pointer_layer += delta_layer;
702 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
704 if (_copy && first_move) {
706 /* duplicate the regionview(s) and region(s) */
708 list<DraggingView> new_regionviews;
710 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
712 RegionView* rv = i->view;
713 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
714 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
716 const boost::shared_ptr<const Region> original = rv->region();
717 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
718 region_copy->set_position (original->position(), this);
722 boost::shared_ptr<AudioRegion> audioregion_copy
723 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
725 nrv = new AudioRegionView (*arv, audioregion_copy);
727 boost::shared_ptr<MidiRegion> midiregion_copy
728 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
729 nrv = new MidiRegionView (*mrv, midiregion_copy);
734 nrv->get_canvas_group()->show ();
735 new_regionviews.push_back (DraggingView (nrv, this));
737 /* swap _primary to the copy */
739 if (rv == _primary) {
743 /* ..and deselect the one we copied */
745 rv->set_selected (false);
748 if (!new_regionviews.empty()) {
750 /* reflect the fact that we are dragging the copies */
752 _views = new_regionviews;
754 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
757 sync the canvas to what we think is its current state
758 without it, the canvas seems to
759 "forget" to update properly after the upcoming reparent()
760 ..only if the mouse is in rapid motion at the time of the grab.
761 something to do with regionview creation taking so long?
763 _editor->update_canvas_now();
767 RegionMotionDrag::motion (event, first_move);
771 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
773 if (!movement_occurred) {
778 /* reverse this here so that we have the correct logic to finalize
782 if (Config->get_edit_mode() == Lock) {
783 _x_constrained = !_x_constrained;
786 assert (!_views.empty ());
788 bool const changed_position = (_last_frame_position != _primary->region()->position());
789 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
790 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
792 _editor->update_canvas_now ();
814 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
816 RegionSelection new_views;
817 PlaylistSet modified_playlists;
818 list<RegionView*> views_to_delete;
821 /* all changes were made during motion event handlers */
823 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
827 _editor->commit_reversible_command ();
831 if (_x_constrained) {
832 _editor->begin_reversible_command (_("fixed time region copy"));
834 _editor->begin_reversible_command (_("region copy"));
837 /* insert the regions into their new playlists */
838 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
840 if (i->view->region()->locked()) {
846 if (changed_position && !_x_constrained) {
847 where = i->view->region()->position() - drag_delta;
849 where = i->view->region()->position();
852 RegionView* new_view = insert_region_into_playlist (
853 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
860 new_views.push_back (new_view);
862 /* we don't need the copied RegionView any more */
863 views_to_delete.push_back (i->view);
866 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
867 because when views are deleted they are automagically removed from _views, which messes
870 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
874 /* If we've created new regions either by copying or moving
875 to a new track, we want to replace the old selection with the new ones
878 if (new_views.size() > 0) {
879 _editor->selection->set (new_views);
882 /* write commands for the accumulated diffs for all our modified playlists */
883 add_stateful_diff_commands_for_playlists (modified_playlists);
885 _editor->commit_reversible_command ();
889 RegionMoveDrag::finished_no_copy (
890 bool const changed_position,
891 bool const changed_tracks,
892 framecnt_t const drag_delta
895 RegionSelection new_views;
896 PlaylistSet modified_playlists;
897 PlaylistSet frozen_playlists;
900 /* all changes were made during motion event handlers */
901 _editor->commit_reversible_command ();
905 if (_x_constrained) {
906 _editor->begin_reversible_command (_("fixed time region drag"));
908 _editor->begin_reversible_command (_("region drag"));
911 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
913 RegionView* rv = i->view;
915 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
916 layer_t const dest_layer = i->layer;
918 if (rv->region()->locked()) {
925 if (changed_position && !_x_constrained) {
926 where = rv->region()->position() - drag_delta;
928 where = rv->region()->position();
931 if (changed_tracks) {
933 /* insert into new playlist */
935 RegionView* new_view = insert_region_into_playlist (
936 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
944 new_views.push_back (new_view);
946 /* remove from old playlist */
948 /* the region that used to be in the old playlist is not
949 moved to the new one - we use a copy of it. as a result,
950 any existing editor for the region should no longer be
953 rv->hide_region_editor();
954 rv->fake_set_opaque (false);
956 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
960 rv->region()->clear_changes ();
963 motion on the same track. plonk the previously reparented region
964 back to its original canvas group (its streamview).
965 No need to do anything for copies as they are fake regions which will be deleted.
968 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
969 rv->get_canvas_group()->property_y() = i->initial_y;
970 rv->get_time_axis_view().reveal_dependent_views (*rv);
972 /* just change the model */
974 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
976 if (dest_rtv->view()->layer_display() == Stacked) {
977 rv->region()->set_layer (dest_layer);
978 rv->region()->set_pending_explicit_relayer (true);
981 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
983 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
989 /* this movement may result in a crossfade being modified, so we need to get undo
990 data from the playlist as well as the region.
993 r = modified_playlists.insert (playlist);
995 playlist->clear_changes ();
998 rv->region()->set_position (where, (void*) this);
1000 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1003 if (changed_tracks) {
1005 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1006 was selected in all of them, then removing it from a playlist will have removed all
1007 trace of it from _views (i.e. there were N regions selected, we removed 1,
1008 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1009 corresponding regionview, and _views is now empty).
1011 This could have invalidated any and all iterators into _views.
1013 The heuristic we use here is: if the region selection is empty, break out of the loop
1014 here. if the region selection is not empty, then restart the loop because we know that
1015 we must have removed at least the region(view) we've just been working on as well as any
1016 that we processed on previous iterations.
1018 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1019 we can just iterate.
1023 if (_views.empty()) {
1034 /* If we've created new regions either by copying or moving
1035 to a new track, we want to replace the old selection with the new ones
1038 if (new_views.size() > 0) {
1039 _editor->selection->set (new_views);
1042 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1046 /* write commands for the accumulated diffs for all our modified playlists */
1047 add_stateful_diff_commands_for_playlists (modified_playlists);
1049 _editor->commit_reversible_command ();
1052 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1053 * @param region Region to remove.
1054 * @param playlist playlist To remove from.
1055 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1056 * that clear_changes () is only called once per playlist.
1059 RegionMoveDrag::remove_region_from_playlist (
1060 boost::shared_ptr<Region> region,
1061 boost::shared_ptr<Playlist> playlist,
1062 PlaylistSet& modified_playlists
1065 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1068 playlist->clear_changes ();
1071 playlist->remove_region (region);
1075 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1076 * clearing the playlist's diff history first if necessary.
1077 * @param region Region to insert.
1078 * @param dest_rtv Destination RouteTimeAxisView.
1079 * @param dest_layer Destination layer.
1080 * @param where Destination position.
1081 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1082 * that clear_changes () is only called once per playlist.
1083 * @return New RegionView, or 0 if no insert was performed.
1086 RegionMoveDrag::insert_region_into_playlist (
1087 boost::shared_ptr<Region> region,
1088 RouteTimeAxisView* dest_rtv,
1091 PlaylistSet& modified_playlists
1094 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1095 if (!dest_playlist) {
1099 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1100 _new_region_view = 0;
1101 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1103 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1104 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1106 dest_playlist->clear_changes ();
1109 dest_playlist->add_region (region, where);
1111 if (dest_rtv->view()->layer_display() == Stacked) {
1112 region->set_layer (dest_layer);
1113 region->set_pending_explicit_relayer (true);
1118 assert (_new_region_view);
1120 return _new_region_view;
1124 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1126 _new_region_view = rv;
1130 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1132 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1133 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1135 _editor->session()->add_command (new StatefulDiffCommand (*i));
1144 RegionMoveDrag::aborted (bool movement_occurred)
1148 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1155 RegionMotionDrag::aborted (movement_occurred);
1160 RegionMotionDrag::aborted (bool)
1162 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1163 RegionView* rv = i->view;
1164 TimeAxisView* tv = &(rv->get_time_axis_view ());
1165 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1167 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1168 rv->get_canvas_group()->property_y() = 0;
1169 rv->get_time_axis_view().reveal_dependent_views (*rv);
1170 rv->fake_set_opaque (false);
1171 rv->move (-_total_x_delta, 0);
1172 rv->set_height (rtv->view()->child_height ());
1175 _editor->update_canvas_now ();
1178 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1179 : RegionMotionDrag (e, i, p, v, b),
1182 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1185 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1186 if (rtv && rtv->is_track()) {
1187 speed = rtv->track()->speed ();
1190 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1194 RegionMoveDrag::setup_pointer_frame_offset ()
1196 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1199 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1200 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1202 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1204 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1205 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1207 _primary = v->view()->create_region_view (r, false, false);
1209 _primary->get_canvas_group()->show ();
1210 _primary->set_position (pos, 0);
1211 _views.push_back (DraggingView (_primary, this));
1213 _last_frame_position = pos;
1215 _item = _primary->get_canvas_group ();
1219 RegionInsertDrag::finished (GdkEvent *, bool)
1221 _editor->update_canvas_now ();
1223 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1225 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1226 _primary->get_canvas_group()->property_y() = 0;
1228 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1230 _editor->begin_reversible_command (_("insert region"));
1231 playlist->clear_changes ();
1232 playlist->add_region (_primary->region (), _last_frame_position);
1233 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1234 _editor->commit_reversible_command ();
1242 RegionInsertDrag::aborted (bool)
1249 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1250 : RegionMoveDrag (e, i, p, v, false, false)
1252 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1255 struct RegionSelectionByPosition {
1256 bool operator() (RegionView*a, RegionView* b) {
1257 return a->region()->position () < b->region()->position();
1262 RegionSpliceDrag::motion (GdkEvent* event, bool)
1264 /* Which trackview is this ? */
1266 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1267 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1268 layer_t layer = tvp.second;
1270 if (tv && tv->layer_display() == Overlaid) {
1274 /* The region motion is only processed if the pointer is over
1278 if (!tv || !tv->is_track()) {
1279 /* To make sure we hide the verbose canvas cursor when the mouse is
1280 not held over and audiotrack.
1282 _editor->hide_verbose_canvas_cursor ();
1288 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1294 RegionSelection copy (_editor->selection->regions);
1296 RegionSelectionByPosition cmp;
1299 framepos_t const pf = adjusted_current_frame (event);
1301 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1303 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1309 boost::shared_ptr<Playlist> playlist;
1311 if ((playlist = atv->playlist()) == 0) {
1315 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1320 if (pf < (*i)->region()->last_frame() + 1) {
1324 if (pf > (*i)->region()->first_frame()) {
1330 playlist->shuffle ((*i)->region(), dir);
1335 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1337 RegionMoveDrag::finished (event, movement_occurred);
1341 RegionSpliceDrag::aborted (bool)
1346 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1348 _view (dynamic_cast<MidiTimeAxisView*> (v))
1350 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1356 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1362 framepos_t const f = adjusted_current_frame (event);
1363 if (f < grab_frame()) {
1364 _region->set_position (f, this);
1367 /* again, don't use a zero-length region (see above) */
1368 framecnt_t const len = abs (f - grab_frame ());
1369 _region->set_length (len < 1 ? 1 : len, this);
1375 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1377 if (!movement_occurred) {
1382 _editor->commit_reversible_command ();
1387 RegionCreateDrag::add_region ()
1389 if (_editor->session()) {
1390 const TempoMap& map (_editor->session()->tempo_map());
1391 framecnt_t pos = grab_frame();
1392 const Meter& m = map.meter_at (pos);
1393 /* not that the frame rate used here can be affected by pull up/down which
1396 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1397 _region = _view->add_region (grab_frame(), len, false);
1402 RegionCreateDrag::aborted (bool)
1407 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1411 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1415 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1417 Gdk::Cursor* cursor;
1418 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1419 float x_fraction = cnote->mouse_x_fraction ();
1421 if (x_fraction > 0.0 && x_fraction < 0.25) {
1422 cursor = _editor->cursors()->left_side_trim;
1424 cursor = _editor->cursors()->right_side_trim;
1427 Drag::start_grab (event, cursor);
1429 region = &cnote->region_view();
1431 double const region_start = region->get_position_pixels();
1432 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1434 if (grab_x() <= middle_point) {
1435 cursor = _editor->cursors()->left_side_trim;
1438 cursor = _editor->cursors()->right_side_trim;
1442 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1444 if (event->motion.state & Keyboard::PrimaryModifier) {
1450 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1452 if (ms.size() > 1) {
1453 /* has to be relative, may make no sense otherwise */
1457 /* select this note; if it is already selected, preserve the existing selection,
1458 otherwise make this note the only one selected.
1460 region->note_selected (cnote, cnote->selected ());
1462 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1463 MidiRegionSelection::iterator next;
1466 (*r)->begin_resizing (at_front);
1472 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1474 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1475 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1476 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1481 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1483 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1484 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1485 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1490 NoteResizeDrag::aborted (bool)
1495 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1498 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1502 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1508 RegionGainDrag::finished (GdkEvent *, bool)
1514 RegionGainDrag::aborted (bool)
1519 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1520 : RegionDrag (e, i, p, v)
1522 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1526 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1529 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1530 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1532 if (tv && tv->is_track()) {
1533 speed = tv->track()->speed();
1536 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1537 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1538 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1540 framepos_t const pf = adjusted_current_frame (event);
1542 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1543 /* Move the contents of the region around without changing the region bounds */
1544 _operation = ContentsTrim;
1545 Drag::start_grab (event, _editor->cursors()->trimmer);
1547 /* These will get overridden for a point trim.*/
1548 if (pf < (region_start + region_length/2)) {
1549 /* closer to front */
1550 _operation = StartTrim;
1551 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1554 _operation = EndTrim;
1555 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1559 switch (_operation) {
1561 _editor->show_verbose_time_cursor (region_start, 10);
1562 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1563 i->view->trim_front_starting ();
1567 _editor->show_verbose_time_cursor (region_end, 10);
1570 _editor->show_verbose_time_cursor (pf, 10);
1574 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1575 i->view->region()->suspend_property_changes ();
1580 TrimDrag::motion (GdkEvent* event, bool first_move)
1582 RegionView* rv = _primary;
1585 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1586 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1587 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1589 if (tv && tv->is_track()) {
1590 speed = tv->track()->speed();
1593 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1599 switch (_operation) {
1601 trim_type = "Region start trim";
1604 trim_type = "Region end trim";
1607 trim_type = "Region content trim";
1611 _editor->begin_reversible_command (trim_type);
1613 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1614 RegionView* rv = i->view;
1615 rv->fake_set_opaque (false);
1616 rv->enable_display (false);
1617 rv->region()->playlist()->clear_owned_changes ();
1619 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1622 arv->temporarily_hide_envelope ();
1625 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1626 insert_result = _editor->motion_frozen_playlists.insert (pl);
1628 if (insert_result.second) {
1634 bool non_overlap_trim = false;
1636 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1637 non_overlap_trim = true;
1640 switch (_operation) {
1642 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1643 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1648 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1649 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1655 bool swap_direction = false;
1657 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1658 swap_direction = true;
1661 framecnt_t frame_delta = 0;
1663 bool left_direction = false;
1664 if (last_pointer_frame() > adjusted_current_frame(event)) {
1665 left_direction = true;
1668 if (left_direction) {
1669 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1671 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1674 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1675 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1681 switch (_operation) {
1683 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->position() / speed), 10);
1686 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->last_frame() / speed), 10);
1689 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1696 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1698 if (movement_occurred) {
1699 motion (event, false);
1701 /* This must happen before the region's StatefulDiffCommand is created, as it may
1702 `correct' (ahem) the region's _start from being negative to being zero. It
1703 needs to be zero in the undo record.
1705 if (_operation == StartTrim) {
1706 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1707 i->view->trim_front_ending ();
1711 if (!_editor->selection->selected (_primary)) {
1712 _primary->thaw_after_trim ();
1715 set<boost::shared_ptr<Playlist> > diffed_playlists;
1717 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1718 i->view->thaw_after_trim ();
1719 i->view->enable_display (true);
1720 i->view->fake_set_opaque (true);
1722 /* Trimming one region may affect others on the playlist, so we need
1723 to get undo Commands from the whole playlist rather than just the
1724 region. Use diffed_playlists to make sure we don't diff a given
1725 playlist more than once.
1727 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1728 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1729 vector<Command*> cmds;
1731 _editor->session()->add_commands (cmds);
1732 diffed_playlists.insert (p);
1736 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1740 _editor->motion_frozen_playlists.clear ();
1741 _editor->commit_reversible_command();
1744 /* no mouse movement */
1745 _editor->point_trim (event, adjusted_current_frame (event));
1748 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1749 if (_operation == StartTrim) {
1750 i->view->trim_front_ending ();
1753 i->view->region()->resume_property_changes ();
1758 TrimDrag::aborted (bool movement_occurred)
1760 /* Our motion method is changing model state, so use the Undo system
1761 to cancel. Perhaps not ideal, as this will leave an Undo point
1762 behind which may be slightly odd from the user's point of view.
1767 if (movement_occurred) {
1771 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1772 i->view->region()->resume_property_changes ();
1777 TrimDrag::setup_pointer_frame_offset ()
1779 list<DraggingView>::iterator i = _views.begin ();
1780 while (i != _views.end() && i->view != _primary) {
1784 if (i == _views.end()) {
1788 switch (_operation) {
1790 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1793 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1800 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1804 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1806 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1811 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1814 // create a dummy marker for visual representation of moving the copy.
1815 // The actual copying is not done before we reach the finish callback.
1817 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1819 MeterMarker* new_marker = new MeterMarker (
1821 *_editor->meter_group,
1822 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1824 *new MeterSection (_marker->meter())
1827 _item = &new_marker->the_item ();
1828 _marker = new_marker;
1832 MetricSection& section (_marker->meter());
1834 if (!section.movable()) {
1840 Drag::start_grab (event, cursor);
1842 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1846 MeterMarkerDrag::setup_pointer_frame_offset ()
1848 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1852 MeterMarkerDrag::motion (GdkEvent* event, bool)
1854 framepos_t const pf = adjusted_current_frame (event);
1856 _marker->set_position (pf);
1858 _editor->show_verbose_time_cursor (pf, 10);
1862 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1864 if (!movement_occurred) {
1868 motion (event, false);
1870 Timecode::BBT_Time when;
1872 TempoMap& map (_editor->session()->tempo_map());
1873 map.bbt_time (last_pointer_frame(), when);
1875 if (_copy == true) {
1876 _editor->begin_reversible_command (_("copy meter mark"));
1877 XMLNode &before = map.get_state();
1878 map.add_meter (_marker->meter(), when);
1879 XMLNode &after = map.get_state();
1880 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1881 _editor->commit_reversible_command ();
1883 // delete the dummy marker we used for visual representation of copying.
1884 // a new visual marker will show up automatically.
1887 _editor->begin_reversible_command (_("move meter mark"));
1888 XMLNode &before = map.get_state();
1889 map.move_meter (_marker->meter(), when);
1890 XMLNode &after = map.get_state();
1891 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1892 _editor->commit_reversible_command ();
1897 MeterMarkerDrag::aborted (bool)
1899 _marker->set_position (_marker->meter().frame ());
1902 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1906 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1908 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1913 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1917 // create a dummy marker for visual representation of moving the copy.
1918 // The actual copying is not done before we reach the finish callback.
1920 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1922 TempoMarker* new_marker = new TempoMarker (
1924 *_editor->tempo_group,
1925 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1927 *new TempoSection (_marker->tempo())
1930 _item = &new_marker->the_item ();
1931 _marker = new_marker;
1935 Drag::start_grab (event, cursor);
1937 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1941 TempoMarkerDrag::setup_pointer_frame_offset ()
1943 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1947 TempoMarkerDrag::motion (GdkEvent* event, bool)
1949 framepos_t const pf = adjusted_current_frame (event);
1950 _marker->set_position (pf);
1951 _editor->show_verbose_time_cursor (pf, 10);
1955 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1957 if (!movement_occurred) {
1961 motion (event, false);
1963 Timecode::BBT_Time when;
1965 TempoMap& map (_editor->session()->tempo_map());
1966 map.bbt_time (last_pointer_frame(), when);
1968 if (_copy == true) {
1969 _editor->begin_reversible_command (_("copy tempo mark"));
1970 XMLNode &before = map.get_state();
1971 map.add_tempo (_marker->tempo(), when);
1972 XMLNode &after = map.get_state();
1973 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1974 _editor->commit_reversible_command ();
1976 // delete the dummy marker we used for visual representation of copying.
1977 // a new visual marker will show up automatically.
1980 _editor->begin_reversible_command (_("move tempo mark"));
1981 XMLNode &before = map.get_state();
1982 map.move_tempo (_marker->tempo(), when);
1983 XMLNode &after = map.get_state();
1984 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1985 _editor->commit_reversible_command ();
1990 TempoMarkerDrag::aborted (bool)
1992 _marker->set_position (_marker->tempo().frame());
1995 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1999 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2002 /** Do all the things we do when dragging the playhead to make it look as though
2003 * we have located, without actually doing the locate (because that would cause
2004 * the diskstream buffers to be refilled, which is too slow).
2007 CursorDrag::fake_locate (framepos_t t)
2009 _editor->playhead_cursor->set_position (t);
2011 Session* s = _editor->session ();
2012 if (s->timecode_transmission_suspended ()) {
2013 framepos_t const f = _editor->playhead_cursor->current_frame;
2014 s->send_mmc_locate (f);
2015 s->send_full_time_code (f);
2018 _editor->show_verbose_time_cursor (t, 10);
2019 _editor->UpdateAllTransportClocks (t);
2023 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2025 Drag::start_grab (event, c);
2027 framepos_t where = _editor->event_frame (event, 0, 0);
2028 _editor->snap_to_with_modifier (where, event);
2030 _editor->_dragging_playhead = true;
2032 Session* s = _editor->session ();
2035 if (_was_rolling && _stop) {
2039 if (s->is_auditioning()) {
2040 s->cancel_audition ();
2043 s->request_suspend_timecode_transmission ();
2044 while (!s->timecode_transmission_suspended ()) {
2045 /* twiddle our thumbs */
2049 fake_locate (where);
2053 CursorDrag::motion (GdkEvent* event, bool)
2055 framepos_t const adjusted_frame = adjusted_current_frame (event);
2057 if (adjusted_frame == last_pointer_frame()) {
2061 fake_locate (adjusted_frame);
2064 _editor->update_canvas_now ();
2069 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2071 _editor->_dragging_playhead = false;
2073 if (!movement_occurred && _stop) {
2077 motion (event, false);
2079 Session* s = _editor->session ();
2081 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2082 _editor->_pending_locate_request = true;
2083 s->request_resume_timecode_transmission ();
2088 CursorDrag::aborted (bool)
2090 if (_editor->_dragging_playhead) {
2091 _editor->session()->request_resume_timecode_transmission ();
2092 _editor->_dragging_playhead = false;
2095 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2098 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2099 : RegionDrag (e, i, p, v)
2101 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2105 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2107 Drag::start_grab (event, cursor);
2109 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2110 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2112 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2114 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2118 FadeInDrag::setup_pointer_frame_offset ()
2120 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2121 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2122 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2126 FadeInDrag::motion (GdkEvent* event, bool)
2128 framecnt_t fade_length;
2130 framepos_t const pos = adjusted_current_frame (event);
2132 boost::shared_ptr<Region> region = _primary->region ();
2134 if (pos < (region->position() + 64)) {
2135 fade_length = 64; // this should be a minimum defined somewhere
2136 } else if (pos > region->last_frame()) {
2137 fade_length = region->length();
2139 fade_length = pos - region->position();
2142 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 (fade_length);
2151 tmp->show_fade_line((framecnt_t) fade_length);
2154 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2158 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2160 if (!movement_occurred) {
2164 framecnt_t fade_length;
2166 framepos_t const pos = adjusted_current_frame (event);
2168 boost::shared_ptr<Region> region = _primary->region ();
2170 if (pos < (region->position() + 64)) {
2171 fade_length = 64; // this should be a minimum defined somewhere
2172 } else if (pos > region->last_frame()) {
2173 fade_length = region->length();
2175 fade_length = pos - region->position();
2178 _editor->begin_reversible_command (_("change fade in length"));
2180 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2182 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2188 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2189 XMLNode &before = alist->get_state();
2191 tmp->audio_region()->set_fade_in_length (fade_length);
2192 tmp->audio_region()->set_fade_in_active (true);
2193 tmp->hide_fade_line();
2195 XMLNode &after = alist->get_state();
2196 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2199 _editor->commit_reversible_command ();
2203 FadeInDrag::aborted (bool)
2205 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2206 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2212 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2213 tmp->hide_fade_line();
2217 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2218 : RegionDrag (e, i, p, v)
2220 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2224 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2226 Drag::start_grab (event, cursor);
2228 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2229 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2231 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2233 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2237 FadeOutDrag::setup_pointer_frame_offset ()
2239 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2240 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2241 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2245 FadeOutDrag::motion (GdkEvent* event, bool)
2247 framecnt_t fade_length;
2249 framepos_t const pos = adjusted_current_frame (event);
2251 boost::shared_ptr<Region> region = _primary->region ();
2253 if (pos > (region->last_frame() - 64)) {
2254 fade_length = 64; // this should really be a minimum fade defined somewhere
2256 else if (pos < region->position()) {
2257 fade_length = region->length();
2260 fade_length = region->last_frame() - pos;
2263 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2265 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2271 tmp->reset_fade_out_shape_width (fade_length);
2272 tmp->show_fade_line(region->length() - fade_length);
2275 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2279 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2281 if (!movement_occurred) {
2285 framecnt_t fade_length;
2287 framepos_t const pos = adjusted_current_frame (event);
2289 boost::shared_ptr<Region> region = _primary->region ();
2291 if (pos > (region->last_frame() - 64)) {
2292 fade_length = 64; // this should really be a minimum fade defined somewhere
2294 else if (pos < region->position()) {
2295 fade_length = region->length();
2298 fade_length = region->last_frame() - pos;
2301 _editor->begin_reversible_command (_("change fade out length"));
2303 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2305 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2311 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2312 XMLNode &before = alist->get_state();
2314 tmp->audio_region()->set_fade_out_length (fade_length);
2315 tmp->audio_region()->set_fade_out_active (true);
2316 tmp->hide_fade_line();
2318 XMLNode &after = alist->get_state();
2319 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2322 _editor->commit_reversible_command ();
2326 FadeOutDrag::aborted (bool)
2328 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2329 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2335 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2336 tmp->hide_fade_line();
2340 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2343 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2345 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2348 _points.push_back (Gnome::Art::Point (0, 0));
2349 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2352 MarkerDrag::~MarkerDrag ()
2354 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2360 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2362 Drag::start_grab (event, cursor);
2366 Location *location = _editor->find_location_from_marker (_marker, is_start);
2367 _editor->_dragging_edit_point = true;
2369 update_item (location);
2371 // _drag_line->show();
2372 // _line->raise_to_top();
2375 _editor->show_verbose_time_cursor (location->start(), 10);
2377 _editor->show_verbose_time_cursor (location->end(), 10);
2380 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2383 case Selection::Toggle:
2384 _editor->selection->toggle (_marker);
2386 case Selection::Set:
2387 if (!_editor->selection->selected (_marker)) {
2388 _editor->selection->set (_marker);
2391 case Selection::Extend:
2393 Locations::LocationList ll;
2394 list<Marker*> to_add;
2396 _editor->selection->markers.range (s, e);
2397 s = min (_marker->position(), s);
2398 e = max (_marker->position(), e);
2401 if (e < max_framepos) {
2404 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2405 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2406 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2409 to_add.push_back (lm->start);
2412 to_add.push_back (lm->end);
2416 if (!to_add.empty()) {
2417 _editor->selection->add (to_add);
2421 case Selection::Add:
2422 _editor->selection->add (_marker);
2426 /* Set up copies for us to manipulate during the drag */
2428 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2429 Location* l = _editor->find_location_from_marker (*i, is_start);
2430 _copied_locations.push_back (new Location (*l));
2435 MarkerDrag::setup_pointer_frame_offset ()
2438 Location *location = _editor->find_location_from_marker (_marker, is_start);
2439 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2443 MarkerDrag::motion (GdkEvent* event, bool)
2445 framecnt_t f_delta = 0;
2447 bool move_both = false;
2449 Location *real_location;
2450 Location *copy_location = 0;
2452 framepos_t const newframe = adjusted_current_frame (event);
2454 framepos_t next = newframe;
2456 if (newframe == last_pointer_frame()) {
2460 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2464 MarkerSelection::iterator i;
2465 list<Location*>::iterator x;
2467 /* find the marker we're dragging, and compute the delta */
2469 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2470 x != _copied_locations.end() && i != _editor->selection->markers.end();
2476 if (marker == _marker) {
2478 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2483 if (real_location->is_mark()) {
2484 f_delta = newframe - copy_location->start();
2488 switch (marker->type()) {
2489 case Marker::SessionStart:
2490 case Marker::RangeStart:
2491 case Marker::LoopStart:
2492 case Marker::PunchIn:
2493 f_delta = newframe - copy_location->start();
2496 case Marker::SessionEnd:
2497 case Marker::RangeEnd:
2498 case Marker::LoopEnd:
2499 case Marker::PunchOut:
2500 f_delta = newframe - copy_location->end();
2503 /* what kind of marker is this ? */
2511 if (i == _editor->selection->markers.end()) {
2512 /* hmm, impossible - we didn't find the dragged marker */
2516 /* now move them all */
2518 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2519 x != _copied_locations.end() && i != _editor->selection->markers.end();
2525 /* call this to find out if its the start or end */
2527 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2531 if (real_location->locked()) {
2535 if (copy_location->is_mark()) {
2539 copy_location->set_start (copy_location->start() + f_delta);
2543 framepos_t new_start = copy_location->start() + f_delta;
2544 framepos_t new_end = copy_location->end() + f_delta;
2546 if (is_start) { // start-of-range marker
2549 copy_location->set_start (new_start);
2550 copy_location->set_end (new_end);
2551 } else if (new_start < copy_location->end()) {
2552 copy_location->set_start (new_start);
2553 } else if (newframe > 0) {
2554 _editor->snap_to (next, 1, true);
2555 copy_location->set_end (next);
2556 copy_location->set_start (newframe);
2559 } else { // end marker
2562 copy_location->set_end (new_end);
2563 copy_location->set_start (new_start);
2564 } else if (new_end > copy_location->start()) {
2565 copy_location->set_end (new_end);
2566 } else if (newframe > 0) {
2567 _editor->snap_to (next, -1, true);
2568 copy_location->set_start (next);
2569 copy_location->set_end (newframe);
2574 update_item (copy_location);
2576 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2579 lm->set_position (copy_location->start(), copy_location->end());
2583 assert (!_copied_locations.empty());
2585 _editor->show_verbose_time_cursor (newframe, 10);
2588 _editor->update_canvas_now ();
2593 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2595 if (!movement_occurred) {
2597 /* just a click, do nothing but finish
2598 off the selection process
2601 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2604 case Selection::Set:
2605 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2606 _editor->selection->set (_marker);
2610 case Selection::Toggle:
2611 case Selection::Extend:
2612 case Selection::Add:
2619 _editor->_dragging_edit_point = false;
2621 _editor->begin_reversible_command ( _("move marker") );
2622 XMLNode &before = _editor->session()->locations()->get_state();
2624 MarkerSelection::iterator i;
2625 list<Location*>::iterator x;
2628 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2629 x != _copied_locations.end() && i != _editor->selection->markers.end();
2632 Location * location = _editor->find_location_from_marker (*i, is_start);
2636 if (location->locked()) {
2640 if (location->is_mark()) {
2641 location->set_start ((*x)->start());
2643 location->set ((*x)->start(), (*x)->end());
2648 XMLNode &after = _editor->session()->locations()->get_state();
2649 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2650 _editor->commit_reversible_command ();
2654 MarkerDrag::aborted (bool)
2660 MarkerDrag::update_item (Location* location)
2665 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2667 _cumulative_x_drag (0),
2668 _cumulative_y_drag (0)
2670 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2672 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2678 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2680 Drag::start_grab (event, _editor->cursors()->fader);
2682 // start the grab at the center of the control point so
2683 // the point doesn't 'jump' to the mouse after the first drag
2684 _fixed_grab_x = _point->get_x();
2685 _fixed_grab_y = _point->get_y();
2687 float const fraction = 1 - (_point->get_y() / _point->line().height());
2689 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2691 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2692 event->button.x + 10, event->button.y + 10);
2694 _editor->show_verbose_canvas_cursor ();
2698 ControlPointDrag::motion (GdkEvent* event, bool)
2700 double dx = _drags->current_pointer_x() - last_pointer_x();
2701 double dy = _drags->current_pointer_y() - last_pointer_y();
2703 if (event->button.state & Keyboard::SecondaryModifier) {
2708 /* coordinate in pixels relative to the start of the region (for region-based automation)
2709 or track (for track-based automation) */
2710 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2711 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2713 // calculate zero crossing point. back off by .01 to stay on the
2714 // positive side of zero
2715 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2717 // make sure we hit zero when passing through
2718 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2722 if (_x_constrained) {
2725 if (_y_constrained) {
2729 _cumulative_x_drag = cx - _fixed_grab_x;
2730 _cumulative_y_drag = cy - _fixed_grab_y;
2734 cy = min ((double) _point->line().height(), cy);
2736 framepos_t cx_frames = _editor->unit_to_frame (cx);
2738 if (!_x_constrained) {
2739 _editor->snap_to_with_modifier (cx_frames, event);
2742 cx_frames = min (cx_frames, _point->line().maximum_time());
2744 float const fraction = 1.0 - (cy / _point->line().height());
2746 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2748 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2750 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2754 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2756 if (!movement_occurred) {
2760 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2761 _editor->reset_point_selection ();
2765 motion (event, false);
2768 _point->line().end_drag ();
2769 _editor->session()->commit_reversible_command ();
2773 ControlPointDrag::aborted (bool)
2775 _point->line().reset ();
2779 ControlPointDrag::active (Editing::MouseMode m)
2781 if (m == Editing::MouseGain) {
2782 /* always active in mouse gain */
2786 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2787 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2790 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2793 _cumulative_y_drag (0)
2795 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2799 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2801 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2804 _item = &_line->grab_item ();
2806 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2807 origin, and ditto for y.
2810 double cx = event->button.x;
2811 double cy = event->button.y;
2813 _line->parent_group().w2i (cx, cy);
2815 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2820 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2821 /* no adjacent points */
2825 Drag::start_grab (event, _editor->cursors()->fader);
2827 /* store grab start in parent frame */
2832 double fraction = 1.0 - (cy / _line->height());
2834 _line->start_drag_line (before, after, fraction);
2836 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2837 event->button.x + 10, event->button.y + 10);
2839 _editor->show_verbose_canvas_cursor ();
2843 LineDrag::motion (GdkEvent* event, bool)
2845 double dy = _drags->current_pointer_y() - last_pointer_y();
2847 if (event->button.state & Keyboard::SecondaryModifier) {
2851 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2853 _cumulative_y_drag = cy - _fixed_grab_y;
2856 cy = min ((double) _line->height(), cy);
2858 double const fraction = 1.0 - (cy / _line->height());
2862 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2868 /* we are ignoring x position for this drag, so we can just pass in anything */
2869 _line->drag_motion (0, fraction, true, push);
2871 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2875 LineDrag::finished (GdkEvent* event, bool)
2877 motion (event, false);
2879 _editor->session()->commit_reversible_command ();
2883 LineDrag::aborted (bool)
2888 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2891 _cumulative_x_drag (0)
2893 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2897 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2899 Drag::start_grab (event);
2901 _line = reinterpret_cast<SimpleLine*> (_item);
2904 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2906 double cx = event->button.x;
2907 double cy = event->button.y;
2909 _item->property_parent().get_value()->w2i(cx, cy);
2911 /* store grab start in parent frame */
2912 _region_view_grab_x = cx;
2914 _before = _line->property_x1();
2916 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2918 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2922 FeatureLineDrag::motion (GdkEvent*, bool)
2924 double dx = _drags->current_pointer_x() - last_pointer_x();
2926 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2928 _cumulative_x_drag += dx;
2930 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2939 _line->property_x1() = cx;
2940 _line->property_x2() = cx;
2942 _before = _line->property_x1();
2946 FeatureLineDrag::finished (GdkEvent*, bool)
2948 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2949 _arv->update_transient(_before, _line->property_x1());
2953 FeatureLineDrag::aborted (bool)
2958 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2961 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2965 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2967 Drag::start_grab (event);
2968 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2972 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2979 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2981 framepos_t grab = grab_frame ();
2982 if (Config->get_rubberbanding_snaps_to_grid ()) {
2983 _editor->snap_to_with_modifier (grab, event);
2986 /* base start and end on initial click position */
2996 if (_drags->current_pointer_y() < grab_y()) {
2997 y1 = _drags->current_pointer_y();
3000 y2 = _drags->current_pointer_y();
3005 if (start != end || y1 != y2) {
3007 double x1 = _editor->frame_to_pixel (start);
3008 double x2 = _editor->frame_to_pixel (end);
3010 _editor->rubberband_rect->property_x1() = x1;
3011 _editor->rubberband_rect->property_y1() = y1;
3012 _editor->rubberband_rect->property_x2() = x2;
3013 _editor->rubberband_rect->property_y2() = y2;
3015 _editor->rubberband_rect->show();
3016 _editor->rubberband_rect->raise_to_top();
3018 _editor->show_verbose_time_cursor (pf, 10);
3023 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3025 if (movement_occurred) {
3027 motion (event, false);
3030 if (_drags->current_pointer_y() < grab_y()) {
3031 y1 = _drags->current_pointer_y();
3034 y2 = _drags->current_pointer_y();
3039 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3042 _editor->begin_reversible_command (_("rubberband selection"));
3044 if (grab_frame() < last_pointer_frame()) {
3045 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3047 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3051 _editor->commit_reversible_command ();
3055 if (!getenv("ARDOUR_SAE")) {
3056 _editor->selection->clear_tracks();
3058 _editor->selection->clear_regions();
3059 _editor->selection->clear_points ();
3060 _editor->selection->clear_lines ();
3063 _editor->rubberband_rect->hide();
3067 RubberbandSelectDrag::aborted (bool)
3069 _editor->rubberband_rect->hide ();
3072 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3073 : RegionDrag (e, i, p, v)
3075 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3079 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3081 Drag::start_grab (event, cursor);
3083 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3087 TimeFXDrag::motion (GdkEvent* event, bool)
3089 RegionView* rv = _primary;
3091 framepos_t const pf = adjusted_current_frame (event);
3093 if (pf > rv->region()->position()) {
3094 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3097 _editor->show_verbose_time_cursor (pf, 10);
3101 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3103 _primary->get_time_axis_view().hide_timestretch ();
3105 if (!movement_occurred) {
3109 if (last_pointer_frame() < _primary->region()->position()) {
3110 /* backwards drag of the left edge - not usable */
3114 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3116 float percentage = (double) newlen / (double) _primary->region()->length();
3118 #ifndef USE_RUBBERBAND
3119 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3120 if (_primary->region()->data_type() == DataType::AUDIO) {
3121 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3125 _editor->begin_reversible_command (_("timestretch"));
3127 // XXX how do timeFX on multiple regions ?
3132 if (_editor->time_stretch (rs, percentage) == -1) {
3133 error << _("An error occurred while executing time stretch operation") << endmsg;
3138 TimeFXDrag::aborted (bool)
3140 _primary->get_time_axis_view().hide_timestretch ();
3143 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3146 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3150 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3152 Drag::start_grab (event);
3156 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3158 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3162 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3164 if (movement_occurred && _editor->session()) {
3165 /* make sure we stop */
3166 _editor->session()->request_transport_speed (0.0);
3171 ScrubDrag::aborted (bool)
3176 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3180 , _original_pointer_time_axis (-1)
3181 , _last_pointer_time_axis (-1)
3183 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3187 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3189 if (_editor->session() == 0) {
3193 Gdk::Cursor* cursor = 0;
3195 switch (_operation) {
3196 case CreateSelection:
3197 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3202 cursor = _editor->cursors()->selector;
3203 Drag::start_grab (event, cursor);
3206 case SelectionStartTrim:
3207 if (_editor->clicked_axisview) {
3208 _editor->clicked_axisview->order_selection_trims (_item, true);
3210 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3213 case SelectionEndTrim:
3214 if (_editor->clicked_axisview) {
3215 _editor->clicked_axisview->order_selection_trims (_item, false);
3217 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3221 Drag::start_grab (event, cursor);
3225 if (_operation == SelectionMove) {
3226 _editor->show_verbose_time_cursor (_editor->selection->time[_editor->clicked_selection].start, 10);
3228 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3231 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3235 SelectionDrag::setup_pointer_frame_offset ()
3237 switch (_operation) {
3238 case CreateSelection:
3239 _pointer_frame_offset = 0;
3242 case SelectionStartTrim:
3244 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3247 case SelectionEndTrim:
3248 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3254 SelectionDrag::motion (GdkEvent* event, bool first_move)
3256 framepos_t start = 0;
3260 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3261 if (pending_time_axis.first == 0) {
3265 framepos_t const pending_position = adjusted_current_frame (event);
3267 /* only alter selection if things have changed */
3269 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3273 switch (_operation) {
3274 case CreateSelection:
3276 framepos_t grab = grab_frame ();
3279 _editor->snap_to (grab);
3282 if (pending_position < grab_frame()) {
3283 start = pending_position;
3286 end = pending_position;
3290 /* first drag: Either add to the selection
3291 or create a new selection
3297 /* adding to the selection */
3298 _editor->set_selected_track_as_side_effect (Selection::Add);
3299 //_editor->selection->add (_editor->clicked_axisview);
3300 _editor->clicked_selection = _editor->selection->add (start, end);
3305 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3306 //_editor->selection->set (_editor->clicked_axisview);
3307 _editor->set_selected_track_as_side_effect (Selection::Set);
3310 _editor->clicked_selection = _editor->selection->set (start, end);
3314 /* select the track that we're in */
3315 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3316 // _editor->set_selected_track_as_side_effect (Selection::Add);
3317 _editor->selection->add (pending_time_axis.first);
3318 _added_time_axes.push_back (pending_time_axis.first);
3321 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3322 tracks that we selected in the first place.
3325 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3326 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3328 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3329 while (i != _added_time_axes.end()) {
3331 list<TimeAxisView*>::iterator tmp = i;
3334 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3335 _editor->selection->remove (*i);
3336 _added_time_axes.remove (*i);
3345 case SelectionStartTrim:
3347 start = _editor->selection->time[_editor->clicked_selection].start;
3348 end = _editor->selection->time[_editor->clicked_selection].end;
3350 if (pending_position > end) {
3353 start = pending_position;
3357 case SelectionEndTrim:
3359 start = _editor->selection->time[_editor->clicked_selection].start;
3360 end = _editor->selection->time[_editor->clicked_selection].end;
3362 if (pending_position < start) {
3365 end = pending_position;
3372 start = _editor->selection->time[_editor->clicked_selection].start;
3373 end = _editor->selection->time[_editor->clicked_selection].end;
3375 length = end - start;
3377 start = pending_position;
3378 _editor->snap_to (start);
3380 end = start + length;
3385 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3386 _editor->start_canvas_autoscroll (1, 0);
3390 _editor->selection->replace (_editor->clicked_selection, start, end);
3393 if (_operation == SelectionMove) {
3394 _editor->show_verbose_time_cursor(start, 10);
3396 _editor->show_verbose_time_cursor(pending_position, 10);
3401 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3403 Session* s = _editor->session();
3405 if (movement_occurred) {
3406 motion (event, false);
3407 /* XXX this is not object-oriented programming at all. ick */
3408 if (_editor->selection->time.consolidate()) {
3409 _editor->selection->TimeChanged ();
3412 /* XXX what if its a music time selection? */
3413 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3414 s->request_play_range (&_editor->selection->time, true);
3419 /* just a click, no pointer movement.*/
3421 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3422 _editor->selection->clear_time();
3425 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3426 _editor->selection->set (_editor->clicked_axisview);
3429 if (s && s->get_play_range () && s->transport_rolling()) {
3430 s->request_stop (false, false);
3435 _editor->stop_canvas_autoscroll ();
3439 SelectionDrag::aborted (bool)
3444 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3449 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3451 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3452 physical_screen_height (_editor->get_window()));
3453 _drag_rect->hide ();
3455 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3456 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3460 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3462 if (_editor->session() == 0) {
3466 Gdk::Cursor* cursor = 0;
3468 if (!_editor->temp_location) {
3469 _editor->temp_location = new Location (*_editor->session());
3472 switch (_operation) {
3473 case CreateRangeMarker:
3474 case CreateTransportMarker:
3475 case CreateCDMarker:
3477 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3482 cursor = _editor->cursors()->selector;
3486 Drag::start_grab (event, cursor);
3488 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3492 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3494 framepos_t start = 0;
3496 ArdourCanvas::SimpleRect *crect;
3498 switch (_operation) {
3499 case CreateRangeMarker:
3500 crect = _editor->range_bar_drag_rect;
3502 case CreateTransportMarker:
3503 crect = _editor->transport_bar_drag_rect;
3505 case CreateCDMarker:
3506 crect = _editor->cd_marker_bar_drag_rect;
3509 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3514 framepos_t const pf = adjusted_current_frame (event);
3516 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3517 framepos_t grab = grab_frame ();
3518 _editor->snap_to (grab);
3520 if (pf < grab_frame()) {
3528 /* first drag: Either add to the selection
3529 or create a new selection.
3534 _editor->temp_location->set (start, end);
3538 update_item (_editor->temp_location);
3540 //_drag_rect->raise_to_top();
3545 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3546 _editor->start_canvas_autoscroll (1, 0);
3550 _editor->temp_location->set (start, end);
3552 double x1 = _editor->frame_to_pixel (start);
3553 double x2 = _editor->frame_to_pixel (end);
3554 crect->property_x1() = x1;
3555 crect->property_x2() = x2;
3557 update_item (_editor->temp_location);
3560 _editor->show_verbose_time_cursor (pf, 10);
3565 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3567 Location * newloc = 0;
3571 if (movement_occurred) {
3572 motion (event, false);
3575 switch (_operation) {
3576 case CreateRangeMarker:
3577 case CreateCDMarker:
3579 _editor->begin_reversible_command (_("new range marker"));
3580 XMLNode &before = _editor->session()->locations()->get_state();
3581 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3582 if (_operation == CreateCDMarker) {
3583 flags = Location::IsRangeMarker | Location::IsCDMarker;
3584 _editor->cd_marker_bar_drag_rect->hide();
3587 flags = Location::IsRangeMarker;
3588 _editor->range_bar_drag_rect->hide();
3590 newloc = new Location (
3591 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3594 _editor->session()->locations()->add (newloc, true);
3595 XMLNode &after = _editor->session()->locations()->get_state();
3596 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3597 _editor->commit_reversible_command ();
3601 case CreateTransportMarker:
3602 // popup menu to pick loop or punch
3603 _editor->new_transport_marker_context_menu (&event->button, _item);
3607 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3609 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3614 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3616 if (end == max_framepos) {
3617 end = _editor->session()->current_end_frame ();
3620 if (start == max_framepos) {
3621 start = _editor->session()->current_start_frame ();
3624 switch (_editor->mouse_mode) {
3626 /* find the two markers on either side and then make the selection from it */
3627 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3631 /* find the two markers on either side of the click and make the range out of it */
3632 _editor->selection->set (start, end);
3641 _editor->stop_canvas_autoscroll ();
3645 RangeMarkerBarDrag::aborted (bool)
3651 RangeMarkerBarDrag::update_item (Location* location)
3653 double const x1 = _editor->frame_to_pixel (location->start());
3654 double const x2 = _editor->frame_to_pixel (location->end());
3656 _drag_rect->property_x1() = x1;
3657 _drag_rect->property_x2() = x2;
3660 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3664 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3668 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3670 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3671 Drag::start_grab (event, _editor->cursors()->zoom_out);
3674 Drag::start_grab (event, _editor->cursors()->zoom_in);
3678 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3682 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3687 framepos_t const pf = adjusted_current_frame (event);
3689 framepos_t grab = grab_frame ();
3690 _editor->snap_to_with_modifier (grab, event);
3692 /* base start and end on initial click position */
3704 _editor->zoom_rect->show();
3705 _editor->zoom_rect->raise_to_top();
3708 _editor->reposition_zoom_rect(start, end);
3710 _editor->show_verbose_time_cursor (pf, 10);
3715 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3717 if (movement_occurred) {
3718 motion (event, false);
3720 if (grab_frame() < last_pointer_frame()) {
3721 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3723 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3726 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3729 _editor->zoom_rect->hide();
3733 MouseZoomDrag::aborted (bool)
3735 _editor->zoom_rect->hide ();
3738 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3740 , _cumulative_dx (0)
3741 , _cumulative_dy (0)
3743 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3745 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3746 _region = &_primary->region_view ();
3747 _note_height = _region->midi_stream_view()->note_height ();
3751 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3753 Drag::start_grab (event);
3755 if (!(_was_selected = _primary->selected())) {
3757 /* tertiary-click means extend selection - we'll do that on button release,
3758 so don't add it here, because otherwise we make it hard to figure
3759 out the "extend-to" range.
3762 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3765 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3768 _region->note_selected (_primary, true);
3770 _region->unique_select (_primary);
3776 /** @return Current total drag x change in frames */
3778 NoteDrag::total_dx () const
3781 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3783 /* primary note time */
3784 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3786 /* new time of the primary note relative to the region position */
3787 frameoffset_t const st = n + dx;
3789 /* snap and return corresponding delta */
3790 return _region->snap_frame_to_frame (st) - n;
3793 /** @return Current total drag y change in notes */
3795 NoteDrag::total_dy () const
3797 /* this is `backwards' to make increasing note number go in the right direction */
3798 double const dy = _drags->current_pointer_y() - grab_y();
3803 if (abs (dy) >= _note_height) {
3805 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3807 ndy = (int8_t) floor (dy / _note_height / 2.0);
3811 /* more positive value = higher pitch and higher y-axis position on track,
3812 which is the inverse of the X-centric geometric universe
3819 NoteDrag::motion (GdkEvent *, bool)
3821 /* Total change in x and y since the start of the drag */
3822 frameoffset_t const dx = total_dx ();
3823 int8_t const dy = -total_dy ();
3825 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3826 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3827 double const tdy = dy * _note_height - _cumulative_dy;
3830 _cumulative_dx += tdx;
3831 _cumulative_dy += tdy;
3833 int8_t note_delta = total_dy();
3835 _region->move_selection (tdx, tdy, note_delta);
3838 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3839 (int) floor (_primary->note()->note() + note_delta));
3841 _editor->show_verbose_canvas_cursor_with (buf);
3846 NoteDrag::finished (GdkEvent* ev, bool moved)
3849 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3851 if (_was_selected) {
3852 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3854 _region->note_deselected (_primary);
3857 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3858 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3860 if (!extend && !add && _region->selection_size() > 1) {
3861 _region->unique_select (_primary);
3862 } else if (extend) {
3863 _region->note_selected (_primary, true, true);
3865 /* it was added during button press */
3870 _region->note_dropped (_primary, total_dx(), total_dy());
3875 NoteDrag::aborted (bool)
3880 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3881 : Drag (editor, item)
3883 , _nothing_to_drag (false)
3885 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3887 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3890 /* get all lines in the automation view */
3891 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3893 /* find those that overlap the ranges being dragged */
3894 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3895 while (i != lines.end ()) {
3896 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3899 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3901 /* check this range against all the AudioRanges that we are using */
3902 list<AudioRange>::const_iterator k = _ranges.begin ();
3903 while (k != _ranges.end()) {
3904 if (k->coverage (r.first, r.second) != OverlapNone) {
3910 /* add it to our list if it overlaps at all */
3911 if (k != _ranges.end()) {
3916 _lines.push_back (n);
3922 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3926 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3928 Drag::start_grab (event, cursor);
3930 /* Get line states before we start changing things */
3931 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3932 i->state = &i->line->get_state ();
3935 if (_ranges.empty()) {
3937 /* No selected time ranges: drag all points */
3938 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3939 uint32_t const N = i->line->npoints ();
3940 for (uint32_t j = 0; j < N; ++j) {
3941 i->points.push_back (i->line->nth (j));
3947 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3949 framecnt_t const half = (i->start + i->end) / 2;
3951 /* find the line that this audio range starts in */
3952 list<Line>::iterator j = _lines.begin();
3953 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3957 if (j != _lines.end()) {
3958 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3960 /* j is the line that this audio range starts in; fade into it;
3961 64 samples length plucked out of thin air.
3964 framepos_t a = i->start + 64;
3969 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3970 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3972 the_list->add (p, the_list->eval (p));
3973 j->line->add_always_in_view (p);
3974 the_list->add (q, the_list->eval (q));
3975 j->line->add_always_in_view (q);
3978 /* same thing for the end */
3981 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3985 if (j != _lines.end()) {
3986 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3988 /* j is the line that this audio range starts in; fade out of it;
3989 64 samples length plucked out of thin air.
3992 framepos_t b = i->end - 64;
3997 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3998 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4000 the_list->add (p, the_list->eval (p));
4001 j->line->add_always_in_view (p);
4002 the_list->add (q, the_list->eval (q));
4003 j->line->add_always_in_view (q);
4007 _nothing_to_drag = true;
4009 /* Find all the points that should be dragged and put them in the relevant
4010 points lists in the Line structs.
4013 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4015 uint32_t const N = i->line->npoints ();
4016 for (uint32_t j = 0; j < N; ++j) {
4018 /* here's a control point on this line */
4019 ControlPoint* p = i->line->nth (j);
4020 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4022 /* see if it's inside a range */
4023 list<AudioRange>::const_iterator k = _ranges.begin ();
4024 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4028 if (k != _ranges.end()) {
4029 /* dragging this point */
4030 _nothing_to_drag = false;
4031 i->points.push_back (p);
4037 if (_nothing_to_drag) {
4041 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4042 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4047 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4049 if (_nothing_to_drag) {
4053 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4054 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4056 /* we are ignoring x position for this drag, so we can just pass in anything */
4057 i->line->drag_motion (0, f, true, false);
4062 AutomationRangeDrag::finished (GdkEvent* event, bool)
4064 if (_nothing_to_drag) {
4068 motion (event, false);
4069 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4070 i->line->end_drag ();
4071 i->line->clear_always_in_view ();
4074 _editor->session()->commit_reversible_command ();
4078 AutomationRangeDrag::aborted (bool)
4080 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4081 i->line->clear_always_in_view ();
4086 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4089 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4090 layer = v->region()->layer ();
4091 initial_y = v->get_canvas_group()->property_y ();
4092 initial_playlist = v->region()->playlist ();
4093 initial_position = v->region()->position ();
4094 initial_end = v->region()->position () + v->region()->length ();
4097 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4101 , _cumulative_dx (0)
4103 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4107 PatchChangeDrag::motion (GdkEvent* ev, bool)
4109 framepos_t f = adjusted_current_frame (ev);
4110 boost::shared_ptr<Region> r = _region_view->region ();
4111 f = max (f, r->position ());
4112 f = min (f, r->last_frame ());
4114 framecnt_t const dxf = f - grab_frame();
4115 double const dxu = _editor->frame_to_unit (dxf);
4116 _patch_change->move (dxu - _cumulative_dx, 0);
4117 _cumulative_dx = dxu;
4121 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4123 if (!movement_occurred) {
4127 boost::shared_ptr<Region> r (_region_view->region ());
4129 framepos_t f = adjusted_current_frame (ev);
4130 f = max (f, r->position ());
4131 f = min (f, r->last_frame ());
4133 _region_view->move_patch_change (
4135 _region_view->frames_to_beats (f - r->position() - r->start())
4140 PatchChangeDrag::aborted (bool)
4142 _patch_change->move (-_cumulative_dx, 0);
4146 PatchChangeDrag::setup_pointer_frame_offset ()
4148 boost::shared_ptr<Region> region = _region_view->region ();
4149 _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_patch_change->patch()->time()) - region->position() + region->start();