2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
26 #include "pbd/memento_command.h"
27 #include "pbd/basename.h"
28 #include "pbd/stateful_diff_command.h"
30 #include "gtkmm2ext/utils.h"
32 #include "ardour/session.h"
33 #include "ardour/dB.h"
34 #include "ardour/region_factory.h"
38 #include "audio_region_view.h"
39 #include "midi_region_view.h"
40 #include "ardour_ui.h"
41 #include "gui_thread.h"
42 #include "control_point.h"
44 #include "region_gain_line.h"
45 #include "editor_drag.h"
46 #include "audio_time_axis.h"
47 #include "midi_time_axis.h"
48 #include "canvas-note.h"
49 #include "selection.h"
50 #include "midi_selection.h"
51 #include "automation_time_axis.h"
53 #include "editor_cursors.h"
54 #include "mouse_cursors.h"
57 using namespace ARDOUR;
60 using namespace Gtkmm2ext;
61 using namespace Editing;
62 using namespace ArdourCanvas;
64 using Gtkmm2ext::Keyboard;
66 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
68 DragManager::DragManager (Editor* e)
71 , _current_pointer_frame (0)
76 DragManager::~DragManager ()
81 /** Call abort for each active drag */
87 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
98 DragManager::add (Drag* d)
100 d->set_manager (this);
101 _drags.push_back (d);
105 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
107 d->set_manager (this);
108 _drags.push_back (d);
113 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
115 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
117 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
118 (*i)->start_grab (e, c);
122 /** Call end_grab for each active drag.
123 * @return true if any drag reported movement having occurred.
126 DragManager::end_grab (GdkEvent* e)
131 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
132 bool const t = (*i)->end_grab (e);
147 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
151 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
153 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
154 bool const t = (*i)->motion_handler (e, from_autoscroll);
165 DragManager::have_item (ArdourCanvas::Item* i) const
167 list<Drag*>::const_iterator j = _drags.begin ();
168 while (j != _drags.end() && (*j)->item () != i) {
172 return j != _drags.end ();
175 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
178 , _pointer_frame_offset (0)
179 , _move_threshold_passed (false)
180 , _raw_grab_frame (0)
182 , _last_pointer_frame (0)
188 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
194 cursor = _editor->which_grabber_cursor ();
197 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
201 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
204 cursor = _editor->which_grabber_cursor ();
207 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
209 if (Keyboard::is_button2_event (&event->button)) {
210 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
211 _y_constrained = true;
212 _x_constrained = false;
214 _y_constrained = false;
215 _x_constrained = true;
218 _x_constrained = false;
219 _y_constrained = false;
222 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
223 setup_pointer_frame_offset ();
224 _grab_frame = adjusted_frame (_raw_grab_frame, event);
225 _last_pointer_frame = _grab_frame;
226 _last_pointer_x = _grab_x;
227 _last_pointer_y = _grab_y;
229 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
233 if (_editor->session() && _editor->session()->transport_rolling()) {
236 _was_rolling = false;
239 switch (_editor->snap_type()) {
240 case SnapToRegionStart:
241 case SnapToRegionEnd:
242 case SnapToRegionSync:
243 case SnapToRegionBoundary:
244 _editor->build_region_boundary_cache ();
251 /** Call to end a drag `successfully'. Ungrabs item and calls
252 * subclass' finished() method.
254 * @param event GDK event, or 0.
255 * @return true if some movement occurred, otherwise false.
258 Drag::end_grab (GdkEvent* event)
260 _editor->stop_canvas_autoscroll ();
262 _item->ungrab (event ? event->button.time : 0);
264 finished (event, _move_threshold_passed);
266 _editor->hide_verbose_canvas_cursor();
268 return _move_threshold_passed;
272 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
276 if (f > _pointer_frame_offset) {
277 pos = f - _pointer_frame_offset;
281 _editor->snap_to_with_modifier (pos, event);
288 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
290 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
294 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
296 /* check to see if we have moved in any way that matters since the last motion event */
297 if (_move_threshold_passed &&
298 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
299 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
303 pair<framecnt_t, int> const threshold = move_threshold ();
305 bool const old_move_threshold_passed = _move_threshold_passed;
307 if (!from_autoscroll && !_move_threshold_passed) {
309 bool const xp = (::llabs (_drags->current_pointer_frame () - _grab_frame) >= threshold.first);
310 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
312 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
315 if (active (_editor->mouse_mode) && _move_threshold_passed) {
317 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
318 if (!from_autoscroll) {
319 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
322 motion (event, _move_threshold_passed != old_move_threshold_passed);
324 _last_pointer_x = _drags->current_pointer_x ();
325 _last_pointer_y = _drags->current_pointer_y ();
326 _last_pointer_frame = adjusted_current_frame (event);
334 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
344 _editor->stop_canvas_autoscroll ();
345 _editor->hide_verbose_canvas_cursor ();
348 struct EditorOrderTimeAxisViewSorter {
349 bool operator() (TimeAxisView* a, TimeAxisView* b) {
350 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
351 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
353 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
357 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
361 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
363 /* Make a list of non-hidden tracks to refer to during the drag */
365 TrackViewList track_views = _editor->track_views;
366 track_views.sort (EditorOrderTimeAxisViewSorter ());
368 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
369 if (!(*i)->hidden()) {
371 _time_axis_views.push_back (*i);
373 TimeAxisView::Children children_list = (*i)->get_child_list ();
374 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
375 _time_axis_views.push_back (j->get());
380 /* the list of views can be empty at this point if this is a region list-insert drag
383 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
384 _views.push_back (DraggingView (*i, this));
387 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
391 RegionDrag::region_going_away (RegionView* v)
393 list<DraggingView>::iterator i = _views.begin ();
394 while (i != _views.end() && i->view != v) {
398 if (i != _views.end()) {
403 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
405 RegionDrag::find_time_axis_view (TimeAxisView* t) const
408 int const N = _time_axis_views.size ();
409 while (i < N && _time_axis_views[i] != t) {
420 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
421 : RegionDrag (e, i, p, v),
430 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
432 Drag::start_grab (event, cursor);
434 _editor->show_verbose_time_cursor (_last_frame_position, 10);
436 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
437 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
438 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
442 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
444 /* compute the amount of pointer motion in frames, and where
445 the region would be if we moved it by that much.
447 *pending_region_position = adjusted_current_frame (event);
449 framepos_t sync_frame;
450 framecnt_t sync_offset;
453 sync_offset = _primary->region()->sync_offset (sync_dir);
455 /* we don't handle a sync point that lies before zero.
457 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
459 sync_frame = *pending_region_position + (sync_dir*sync_offset);
461 _editor->snap_to_with_modifier (sync_frame, event);
463 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
466 *pending_region_position = _last_frame_position;
469 if (*pending_region_position > max_framepos - _primary->region()->length()) {
470 *pending_region_position = _last_frame_position;
475 /* in locked edit mode, reverse the usual meaning of _x_constrained */
476 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
478 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
480 /* x movement since last time */
481 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
483 /* total x movement */
484 framecnt_t total_dx = *pending_region_position;
485 if (regions_came_from_canvas()) {
486 total_dx = total_dx - grab_frame () + _pointer_frame_offset;
489 /* check that no regions have gone off the start of the session */
490 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
491 if ((i->view->region()->position() + total_dx) < 0) {
493 *pending_region_position = _last_frame_position;
498 _last_frame_position = *pending_region_position;
505 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
507 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
508 int const n = i->time_axis_view + delta_track;
509 if (n < 0 || n >= int (_time_axis_views.size())) {
510 /* off the top or bottom track */
514 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
515 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
516 /* not a track, or the wrong type */
520 int const l = i->layer + delta_layer;
521 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
522 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
523 If it has, the layers will be munged later anyway, so it's ok.
529 /* all regions being dragged are ok with this change */
534 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
536 assert (!_views.empty ());
538 /* Find the TimeAxisView that the pointer is now over */
539 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
541 /* Bail early if we're not over a track */
542 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
543 if (!rtv || !rtv->is_track()) {
544 _editor->hide_verbose_canvas_cursor ();
548 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
550 /* Here's the current pointer position in terms of time axis view and layer */
551 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
552 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
554 /* Work out the change in x */
555 framepos_t pending_region_position;
556 double const x_delta = compute_x_delta (event, &pending_region_position);
558 /* Work out the change in y */
559 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
560 int delta_layer = current_pointer_layer - _last_pointer_layer;
562 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
563 /* this y movement is not allowed, so do no y movement this time */
564 delta_time_axis_view = 0;
568 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
569 /* haven't reached next snap point, and we're not switching
570 trackviews nor layers. nothing to do.
575 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
577 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
579 RegionView* rv = i->view;
581 if (rv->region()->locked()) {
587 /* here we are calculating the y distance from the
588 top of the first track view to the top of the region
589 area of the track view that we're working on */
591 /* this x value is just a dummy value so that we have something
596 /* distance from the top of this track view to the region area
597 of our track view is always 1 */
601 /* convert to world coordinates, ie distance from the top of
604 rv->get_canvas_frame()->i2w (ix1, iy1);
606 /* compensate for the ruler section and the vertical scrollbar position */
607 iy1 += _editor->get_trackview_group_vertical_offset ();
609 // hide any dependent views
611 rv->get_time_axis_view().hide_dependent_views (*rv);
614 reparent to a non scrolling group so that we can keep the
615 region selection above all time axis views.
616 reparenting means we have to move the rv as the two
617 parent groups have different coordinates.
620 rv->get_canvas_group()->property_y() = iy1 - 1;
621 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
623 rv->fake_set_opaque (true);
626 /* Work out the change in y position of this region view */
630 /* If we have moved tracks, we'll fudge the layer delta so that the
631 region gets moved back onto layer 0 on its new track; this avoids
632 confusion when dragging regions from non-zero layers onto different
635 int this_delta_layer = delta_layer;
636 if (delta_time_axis_view != 0) {
637 this_delta_layer = - i->layer;
640 /* Move this region to layer 0 on its old track */
641 StreamView* lv = _time_axis_views[i->time_axis_view]->view ();
642 if (lv->layer_display() == Stacked) {
643 y_delta -= (lv->layers() - i->layer - 1) * lv->child_height ();
646 /* Now move it to its right layer on the current track */
647 StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view ();
648 if (cv->layer_display() == Stacked) {
649 y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height ();
653 if (delta_time_axis_view > 0) {
654 for (int j = 0; j < delta_time_axis_view; ++j) {
655 y_delta += _time_axis_views[i->time_axis_view + j]->current_height ();
658 /* start by subtracting the height of the track above where we are now */
659 for (int j = 1; j <= -delta_time_axis_view; ++j) {
660 y_delta -= _time_axis_views[i->time_axis_view - j]->current_height ();
665 rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ());
667 /* Update the DraggingView */
668 i->time_axis_view += delta_time_axis_view;
669 i->layer += this_delta_layer;
672 _editor->mouse_brush_insert_region (rv, pending_region_position);
674 rv->move (x_delta, y_delta);
677 } /* foreach region */
679 _total_x_delta += x_delta;
682 _editor->cursor_group->raise_to_top();
685 if (x_delta != 0 && !_brushing) {
686 _editor->show_verbose_time_cursor (_last_frame_position, 10);
689 _last_pointer_time_axis_view += delta_time_axis_view;
690 _last_pointer_layer += delta_layer;
694 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
696 if (_copy && first_move) {
698 /* duplicate the regionview(s) and region(s) */
700 list<DraggingView> new_regionviews;
702 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
704 RegionView* rv = i->view;
705 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
706 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
708 const boost::shared_ptr<const Region> original = rv->region();
709 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
710 region_copy->set_position (original->position(), this);
714 boost::shared_ptr<AudioRegion> audioregion_copy
715 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
717 nrv = new AudioRegionView (*arv, audioregion_copy);
719 boost::shared_ptr<MidiRegion> midiregion_copy
720 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
721 nrv = new MidiRegionView (*mrv, midiregion_copy);
726 nrv->get_canvas_group()->show ();
727 new_regionviews.push_back (DraggingView (nrv, this));
729 /* swap _primary to the copy */
731 if (rv == _primary) {
735 /* ..and deselect the one we copied */
737 rv->set_selected (false);
740 if (!new_regionviews.empty()) {
742 /* reflect the fact that we are dragging the copies */
744 _views = new_regionviews;
746 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
749 sync the canvas to what we think is its current state
750 without it, the canvas seems to
751 "forget" to update properly after the upcoming reparent()
752 ..only if the mouse is in rapid motion at the time of the grab.
753 something to do with regionview creation taking so long?
755 _editor->update_canvas_now();
759 RegionMotionDrag::motion (event, first_move);
763 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
765 if (!movement_occurred) {
770 /* reverse this here so that we have the correct logic to finalize
774 if (Config->get_edit_mode() == Lock) {
775 _x_constrained = !_x_constrained;
778 assert (!_views.empty ());
780 bool const changed_position = (_last_frame_position != _primary->region()->position());
781 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
782 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
784 _editor->update_canvas_now ();
806 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
808 RegionSelection new_views;
809 PlaylistSet modified_playlists;
810 list<RegionView*> views_to_delete;
813 /* all changes were made during motion event handlers */
815 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
819 _editor->commit_reversible_command ();
823 if (_x_constrained) {
824 _editor->begin_reversible_command (_("fixed time region copy"));
826 _editor->begin_reversible_command (_("region copy"));
829 /* insert the regions into their new playlists */
830 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
832 if (i->view->region()->locked()) {
838 if (changed_position && !_x_constrained) {
839 where = i->view->region()->position() - drag_delta;
841 where = i->view->region()->position();
844 RegionView* new_view = insert_region_into_playlist (
845 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
852 new_views.push_back (new_view);
854 /* we don't need the copied RegionView any more */
855 views_to_delete.push_back (i->view);
858 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
859 because when views are deleted they are automagically removed from _views, which messes
862 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
866 /* If we've created new regions either by copying or moving
867 to a new track, we want to replace the old selection with the new ones
870 if (new_views.size() > 0) {
871 _editor->selection->set (new_views);
874 /* write commands for the accumulated diffs for all our modified playlists */
875 add_stateful_diff_commands_for_playlists (modified_playlists);
877 _editor->commit_reversible_command ();
881 RegionMoveDrag::finished_no_copy (
882 bool const changed_position,
883 bool const changed_tracks,
884 framecnt_t const drag_delta
887 RegionSelection new_views;
888 PlaylistSet modified_playlists;
889 PlaylistSet frozen_playlists;
892 /* all changes were made during motion event handlers */
893 _editor->commit_reversible_command ();
897 if (_x_constrained) {
898 _editor->begin_reversible_command (_("fixed time region drag"));
900 _editor->begin_reversible_command (_("region drag"));
903 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
905 RegionView* rv = i->view;
907 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
908 layer_t const dest_layer = i->layer;
910 if (rv->region()->locked()) {
917 if (changed_position && !_x_constrained) {
918 where = rv->region()->position() - drag_delta;
920 where = rv->region()->position();
923 if (changed_tracks) {
925 /* insert into new playlist */
927 RegionView* new_view = insert_region_into_playlist (
928 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
936 new_views.push_back (new_view);
938 /* remove from old playlist */
940 /* the region that used to be in the old playlist is not
941 moved to the new one - we use a copy of it. as a result,
942 any existing editor for the region should no longer be
945 rv->hide_region_editor();
946 rv->fake_set_opaque (false);
948 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
952 rv->region()->clear_changes ();
955 motion on the same track. plonk the previously reparented region
956 back to its original canvas group (its streamview).
957 No need to do anything for copies as they are fake regions which will be deleted.
960 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
961 rv->get_canvas_group()->property_y() = i->initial_y;
962 rv->get_time_axis_view().reveal_dependent_views (*rv);
964 /* just change the model */
966 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
968 if (dest_rtv->view()->layer_display() == Stacked) {
969 rv->region()->set_layer (dest_layer);
970 rv->region()->set_pending_explicit_relayer (true);
973 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
975 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
981 /* this movement may result in a crossfade being modified, so we need to get undo
982 data from the playlist as well as the region.
985 r = modified_playlists.insert (playlist);
987 playlist->clear_changes ();
990 rv->region()->set_position (where, (void*) this);
992 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
995 if (changed_tracks) {
997 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
998 was selected in all of them, then removing it from a playlist will have removed all
999 trace of it from _views (i.e. there were N regions selected, we removed 1,
1000 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1001 corresponding regionview, and _views is now empty).
1003 This could have invalidated any and all iterators into _views.
1005 The heuristic we use here is: if the region selection is empty, break out of the loop
1006 here. if the region selection is not empty, then restart the loop because we know that
1007 we must have removed at least the region(view) we've just been working on as well as any
1008 that we processed on previous iterations.
1010 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1011 we can just iterate.
1015 if (_views.empty()) {
1026 /* If we've created new regions either by copying or moving
1027 to a new track, we want to replace the old selection with the new ones
1030 if (new_views.size() > 0) {
1031 _editor->selection->set (new_views);
1034 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1038 /* write commands for the accumulated diffs for all our modified playlists */
1039 add_stateful_diff_commands_for_playlists (modified_playlists);
1041 _editor->commit_reversible_command ();
1044 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1045 * @param region Region to remove.
1046 * @param playlist playlist To remove from.
1047 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1048 * that clear_changes () is only called once per playlist.
1051 RegionMoveDrag::remove_region_from_playlist (
1052 boost::shared_ptr<Region> region,
1053 boost::shared_ptr<Playlist> playlist,
1054 PlaylistSet& modified_playlists
1057 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1060 playlist->clear_changes ();
1063 playlist->remove_region (region);
1067 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1068 * clearing the playlist's diff history first if necessary.
1069 * @param region Region to insert.
1070 * @param dest_rtv Destination RouteTimeAxisView.
1071 * @param dest_layer Destination layer.
1072 * @param where Destination position.
1073 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1074 * that clear_changes () is only called once per playlist.
1075 * @return New RegionView, or 0 if no insert was performed.
1078 RegionMoveDrag::insert_region_into_playlist (
1079 boost::shared_ptr<Region> region,
1080 RouteTimeAxisView* dest_rtv,
1083 PlaylistSet& modified_playlists
1086 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1087 if (!dest_playlist) {
1091 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1092 _new_region_view = 0;
1093 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1095 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1096 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1098 dest_playlist->clear_changes ();
1101 dest_playlist->add_region (region, where);
1103 if (dest_rtv->view()->layer_display() == Stacked) {
1104 region->set_layer (dest_layer);
1105 region->set_pending_explicit_relayer (true);
1110 assert (_new_region_view);
1112 return _new_region_view;
1116 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1118 _new_region_view = rv;
1122 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1124 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1125 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1127 _editor->session()->add_command (new StatefulDiffCommand (*i));
1136 RegionMoveDrag::aborted ()
1140 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1147 RegionMotionDrag::aborted ();
1152 RegionMotionDrag::aborted ()
1154 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1155 RegionView* rv = i->view;
1156 TimeAxisView* tv = &(rv->get_time_axis_view ());
1157 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1159 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1160 rv->get_canvas_group()->property_y() = 0;
1161 rv->get_time_axis_view().reveal_dependent_views (*rv);
1162 rv->fake_set_opaque (false);
1163 rv->move (-_total_x_delta, 0);
1164 rv->set_height (rtv->view()->child_height ());
1167 _editor->update_canvas_now ();
1170 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1171 : RegionMotionDrag (e, i, p, v, b),
1174 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1177 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1178 if (rtv && rtv->is_track()) {
1179 speed = rtv->track()->speed ();
1182 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1186 RegionMoveDrag::setup_pointer_frame_offset ()
1188 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1191 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1192 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1194 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1196 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1197 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1199 _primary = v->view()->create_region_view (r, false, false);
1201 _primary->get_canvas_group()->show ();
1202 _primary->set_position (pos, 0);
1203 _views.push_back (DraggingView (_primary, this));
1205 _last_frame_position = pos;
1207 _item = _primary->get_canvas_group ();
1211 RegionInsertDrag::finished (GdkEvent *, bool)
1213 _editor->update_canvas_now ();
1215 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1217 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1218 _primary->get_canvas_group()->property_y() = 0;
1220 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1222 _editor->begin_reversible_command (_("insert region"));
1223 playlist->clear_changes ();
1224 playlist->add_region (_primary->region (), _last_frame_position);
1225 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1226 _editor->commit_reversible_command ();
1234 RegionInsertDrag::aborted ()
1241 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1242 : RegionMoveDrag (e, i, p, v, false, false)
1244 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1247 struct RegionSelectionByPosition {
1248 bool operator() (RegionView*a, RegionView* b) {
1249 return a->region()->position () < b->region()->position();
1254 RegionSpliceDrag::motion (GdkEvent* event, bool)
1256 /* Which trackview is this ? */
1258 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1259 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1260 layer_t layer = tvp.second;
1262 if (tv && tv->layer_display() == Overlaid) {
1266 /* The region motion is only processed if the pointer is over
1270 if (!tv || !tv->is_track()) {
1271 /* To make sure we hide the verbose canvas cursor when the mouse is
1272 not held over and audiotrack.
1274 _editor->hide_verbose_canvas_cursor ();
1280 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1286 RegionSelection copy (_editor->selection->regions);
1288 RegionSelectionByPosition cmp;
1291 framepos_t const pf = adjusted_current_frame (event);
1293 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1295 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1301 boost::shared_ptr<Playlist> playlist;
1303 if ((playlist = atv->playlist()) == 0) {
1307 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1312 if (pf < (*i)->region()->last_frame() + 1) {
1316 if (pf > (*i)->region()->first_frame()) {
1322 playlist->shuffle ((*i)->region(), dir);
1327 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1329 RegionMoveDrag::finished (event, movement_occurred);
1333 RegionSpliceDrag::aborted ()
1338 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1340 _view (dynamic_cast<MidiTimeAxisView*> (v))
1342 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1348 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1354 framepos_t const f = adjusted_current_frame (event);
1355 if (f < grab_frame()) {
1356 _region->set_position (f, this);
1359 /* again, don't use a zero-length region (see above) */
1360 framecnt_t const len = abs (f - grab_frame ());
1361 _region->set_length (len < 1 ? 1 : len, this);
1367 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1369 if (!movement_occurred) {
1374 _editor->commit_reversible_command ();
1379 RegionCreateDrag::add_region ()
1381 if (_editor->session()) {
1382 const TempoMap& map (_editor->session()->tempo_map());
1383 framecnt_t pos = grab_frame();
1384 const Meter& m = map.meter_at (pos);
1385 /* not that the frame rate used here can be affected by pull up/down which
1388 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1389 _region = _view->add_region (grab_frame(), len, false);
1394 RegionCreateDrag::aborted ()
1399 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1403 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1407 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1409 Gdk::Cursor* cursor;
1410 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1411 float x_fraction = cnote->mouse_x_fraction ();
1413 if (x_fraction > 0.0 && x_fraction < 0.25) {
1414 cursor = _editor->cursors()->left_side_trim;
1416 cursor = _editor->cursors()->right_side_trim;
1419 Drag::start_grab (event, cursor);
1421 region = &cnote->region_view();
1423 double const region_start = region->get_position_pixels();
1424 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1426 if (grab_x() <= middle_point) {
1427 cursor = _editor->cursors()->left_side_trim;
1430 cursor = _editor->cursors()->right_side_trim;
1434 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1436 if (event->motion.state & Keyboard::PrimaryModifier) {
1442 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1444 if (ms.size() > 1) {
1445 /* has to be relative, may make no sense otherwise */
1449 /* select this note; if it is already selected, preserve the existing selection,
1450 otherwise make this note the only one selected.
1452 region->note_selected (cnote, cnote->selected ());
1454 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1455 MidiRegionSelection::iterator next;
1458 (*r)->begin_resizing (at_front);
1464 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1466 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1467 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1468 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1473 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1475 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1476 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1477 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1482 NoteResizeDrag::aborted ()
1487 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1490 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1494 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1500 RegionGainDrag::finished (GdkEvent *, bool)
1506 RegionGainDrag::aborted ()
1511 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1512 : RegionDrag (e, i, p, v)
1513 , _have_transaction (false)
1515 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1519 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1522 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1523 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1525 if (tv && tv->is_track()) {
1526 speed = tv->track()->speed();
1529 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1530 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1531 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1533 framepos_t const pf = adjusted_current_frame (event);
1535 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1536 /* Move the contents of the region around without changing the region bounds */
1537 _operation = ContentsTrim;
1538 Drag::start_grab (event, _editor->cursors()->trimmer);
1540 /* These will get overridden for a point trim.*/
1541 if (pf < (region_start + region_length/2)) {
1542 /* closer to front */
1543 _operation = StartTrim;
1544 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1547 _operation = EndTrim;
1548 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1552 switch (_operation) {
1554 _editor->show_verbose_time_cursor (region_start, 10);
1555 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1556 i->view->trim_front_starting ();
1560 _editor->show_verbose_time_cursor (region_end, 10);
1563 _editor->show_verbose_time_cursor (pf, 10);
1567 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1568 i->view->region()->suspend_property_changes ();
1573 TrimDrag::motion (GdkEvent* event, bool first_move)
1575 RegionView* rv = _primary;
1578 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1579 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1580 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1582 if (tv && tv->is_track()) {
1583 speed = tv->track()->speed();
1586 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1592 switch (_operation) {
1594 trim_type = "Region start trim";
1597 trim_type = "Region end trim";
1600 trim_type = "Region content trim";
1604 _editor->begin_reversible_command (trim_type);
1605 _have_transaction = true;
1607 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1608 RegionView* rv = i->view;
1609 rv->fake_set_opaque (false);
1610 rv->enable_display (false);
1611 rv->region()->clear_changes ();
1613 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1616 arv->temporarily_hide_envelope ();
1619 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1620 insert_result = _editor->motion_frozen_playlists.insert (pl);
1622 if (insert_result.second) {
1628 bool non_overlap_trim = false;
1630 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1631 non_overlap_trim = true;
1634 switch (_operation) {
1636 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1637 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1642 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1643 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1649 bool swap_direction = false;
1651 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1652 swap_direction = true;
1655 framecnt_t frame_delta = 0;
1657 bool left_direction = false;
1658 if (last_pointer_frame() > adjusted_current_frame(event)) {
1659 left_direction = true;
1662 if (left_direction) {
1663 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1665 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1668 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1669 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1675 switch (_operation) {
1677 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->position() / speed), 10);
1680 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->last_frame() / speed), 10);
1683 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1690 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1692 if (movement_occurred) {
1693 motion (event, false);
1695 /* This must happen before the region's StatefulDiffCommand is created, as it may
1696 `correct' (ahem) the region's _start from being negative to being zero. It
1697 needs to be zero in the undo record.
1699 if (_operation == StartTrim) {
1700 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1701 i->view->trim_front_ending ();
1705 if (!_editor->selection->selected (_primary)) {
1706 _primary->thaw_after_trim ();
1709 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1710 i->view->thaw_after_trim ();
1711 i->view->enable_display (true);
1712 i->view->fake_set_opaque (true);
1713 if (_have_transaction) {
1714 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1718 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1722 _editor->motion_frozen_playlists.clear ();
1723 if (_have_transaction) {
1724 _editor->commit_reversible_command();
1728 /* no mouse movement */
1729 _editor->point_trim (event, adjusted_current_frame (event));
1732 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1733 if (_operation == StartTrim) {
1734 i->view->trim_front_ending ();
1737 i->view->region()->resume_property_changes ();
1742 TrimDrag::aborted ()
1744 /* Our motion method is changing model state, so use the Undo system
1745 to cancel. Perhaps not ideal, as this will leave an Undo point
1746 behind which may be slightly odd from the user's point of view.
1751 if (_have_transaction) {
1755 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1756 i->view->region()->resume_property_changes ();
1761 TrimDrag::setup_pointer_frame_offset ()
1763 list<DraggingView>::iterator i = _views.begin ();
1764 while (i != _views.end() && i->view != _primary) {
1768 if (i == _views.end()) {
1772 switch (_operation) {
1774 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1777 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1782 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1786 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1788 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1793 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1796 // create a dummy marker for visual representation of moving the copy.
1797 // The actual copying is not done before we reach the finish callback.
1799 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1801 MeterMarker* new_marker = new MeterMarker (
1803 *_editor->meter_group,
1804 *_editor->cursor_group,
1805 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1807 *new MeterSection (_marker->meter())
1810 _item = &new_marker->the_item ();
1811 _marker = new_marker;
1815 MetricSection& section (_marker->meter());
1817 if (!section.movable()) {
1823 Drag::start_grab (event, cursor);
1825 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1829 MeterMarkerDrag::setup_pointer_frame_offset ()
1831 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1835 MeterMarkerDrag::motion (GdkEvent* event, bool)
1837 framepos_t const pf = adjusted_current_frame (event);
1839 _marker->set_position (pf);
1841 _editor->show_verbose_time_cursor (pf, 10);
1845 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1847 if (!movement_occurred) {
1851 motion (event, false);
1853 Timecode::BBT_Time when;
1855 TempoMap& map (_editor->session()->tempo_map());
1856 map.bbt_time (last_pointer_frame(), when);
1858 if (_copy == true) {
1859 _editor->begin_reversible_command (_("copy meter mark"));
1860 XMLNode &before = map.get_state();
1861 map.add_meter (_marker->meter(), when);
1862 XMLNode &after = map.get_state();
1863 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1864 _editor->commit_reversible_command ();
1866 // delete the dummy marker we used for visual representation of copying.
1867 // a new visual marker will show up automatically.
1870 _editor->begin_reversible_command (_("move meter mark"));
1871 XMLNode &before = map.get_state();
1872 map.move_meter (_marker->meter(), when);
1873 XMLNode &after = map.get_state();
1874 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1875 _editor->commit_reversible_command ();
1880 MeterMarkerDrag::aborted ()
1882 _marker->set_position (_marker->meter().frame ());
1885 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1889 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1891 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1896 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1900 // create a dummy marker for visual representation of moving the copy.
1901 // The actual copying is not done before we reach the finish callback.
1903 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1905 TempoMarker* new_marker = new TempoMarker (
1907 *_editor->tempo_group,
1908 *_editor->cursor_group,
1909 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1911 *new TempoSection (_marker->tempo())
1914 _item = &new_marker->the_item ();
1915 _marker = new_marker;
1919 Drag::start_grab (event, cursor);
1921 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1925 TempoMarkerDrag::setup_pointer_frame_offset ()
1927 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1931 TempoMarkerDrag::motion (GdkEvent* event, bool)
1933 framepos_t const pf = adjusted_current_frame (event);
1934 _marker->set_position (pf);
1935 _editor->show_verbose_time_cursor (pf, 10);
1939 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1941 if (!movement_occurred) {
1945 motion (event, false);
1947 Timecode::BBT_Time when;
1949 TempoMap& map (_editor->session()->tempo_map());
1950 map.bbt_time (last_pointer_frame(), when);
1952 if (_copy == true) {
1953 _editor->begin_reversible_command (_("copy tempo mark"));
1954 XMLNode &before = map.get_state();
1955 map.add_tempo (_marker->tempo(), when);
1956 XMLNode &after = map.get_state();
1957 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1958 _editor->commit_reversible_command ();
1960 // delete the dummy marker we used for visual representation of copying.
1961 // a new visual marker will show up automatically.
1964 _editor->begin_reversible_command (_("move tempo mark"));
1965 XMLNode &before = map.get_state();
1966 map.move_tempo (_marker->tempo(), when);
1967 XMLNode &after = map.get_state();
1968 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1969 _editor->commit_reversible_command ();
1974 TempoMarkerDrag::aborted ()
1976 _marker->set_position (_marker->tempo().frame());
1979 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1983 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
1987 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1989 Drag::start_grab (event, c);
1992 framepos_t where = _editor->event_frame (event, 0, 0);
1994 _editor->snap_to_with_modifier (where, event);
1995 _editor->playhead_cursor->set_position (where);
1998 _editor->_dragging_playhead = true;
2000 Session* s = _editor->session ();
2003 if (_was_rolling && _stop) {
2007 if (s->is_auditioning()) {
2008 s->cancel_audition ();
2011 s->request_suspend_timecode_transmission ();
2013 if (s->timecode_transmission_suspended ()) {
2014 framepos_t const f = _editor->playhead_cursor->current_frame;
2015 s->send_mmc_locate (f);
2016 s->send_full_time_code (f);
2020 _editor->show_verbose_time_cursor (_editor->playhead_cursor->current_frame, 10);
2021 _editor->UpdateAllTransportClocks (_editor->playhead_cursor->current_frame);
2025 CursorDrag::motion (GdkEvent* event, bool)
2027 framepos_t const adjusted_frame = adjusted_current_frame (event);
2029 if (adjusted_frame == last_pointer_frame()) {
2033 _editor->playhead_cursor->set_position (adjusted_frame);
2035 _editor->show_verbose_time_cursor (_editor->playhead_cursor->current_frame, 10);
2037 Session* s = _editor->session ();
2038 if (s && s->timecode_transmission_suspended ()) {
2039 framepos_t const f = _editor->playhead_cursor->current_frame;
2040 s->send_mmc_locate (f);
2041 s->send_full_time_code (f);
2046 _editor->update_canvas_now ();
2048 _editor->UpdateAllTransportClocks (_editor->playhead_cursor->current_frame);
2052 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2054 _editor->_dragging_playhead = false;
2056 if (!movement_occurred && _stop) {
2060 motion (event, false);
2062 Session* s = _editor->session ();
2064 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2065 _editor->_pending_locate_request = true;
2066 s->request_resume_timecode_transmission ();
2071 CursorDrag::aborted ()
2073 if (_editor->_dragging_playhead) {
2074 _editor->session()->request_resume_timecode_transmission ();
2075 _editor->_dragging_playhead = false;
2078 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2081 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2082 : RegionDrag (e, i, p, v)
2084 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2088 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2090 Drag::start_grab (event, cursor);
2092 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2093 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2095 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2097 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2101 FadeInDrag::setup_pointer_frame_offset ()
2103 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2104 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2105 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2109 FadeInDrag::motion (GdkEvent* event, bool)
2111 framecnt_t fade_length;
2113 framepos_t const pos = adjusted_current_frame (event);
2115 boost::shared_ptr<Region> region = _primary->region ();
2117 if (pos < (region->position() + 64)) {
2118 fade_length = 64; // this should be a minimum defined somewhere
2119 } else if (pos > region->last_frame()) {
2120 fade_length = region->length();
2122 fade_length = pos - region->position();
2125 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2127 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2133 tmp->reset_fade_in_shape_width (fade_length);
2134 tmp->show_fade_line((framecnt_t) fade_length);
2137 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2141 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2143 if (!movement_occurred) {
2147 framecnt_t fade_length;
2149 framepos_t const pos = adjusted_current_frame (event);
2151 boost::shared_ptr<Region> region = _primary->region ();
2153 if (pos < (region->position() + 64)) {
2154 fade_length = 64; // this should be a minimum defined somewhere
2155 } else if (pos > region->last_frame()) {
2156 fade_length = region->length();
2158 fade_length = pos - region->position();
2161 _editor->begin_reversible_command (_("change fade in length"));
2163 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2165 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2171 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2172 XMLNode &before = alist->get_state();
2174 tmp->audio_region()->set_fade_in_length (fade_length);
2175 tmp->audio_region()->set_fade_in_active (true);
2176 tmp->hide_fade_line();
2178 XMLNode &after = alist->get_state();
2179 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2182 _editor->commit_reversible_command ();
2186 FadeInDrag::aborted ()
2188 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2189 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2195 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2196 tmp->hide_fade_line();
2200 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2201 : RegionDrag (e, i, p, v)
2203 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2207 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2209 Drag::start_grab (event, cursor);
2211 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2212 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2214 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2216 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2220 FadeOutDrag::setup_pointer_frame_offset ()
2222 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2223 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2224 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2228 FadeOutDrag::motion (GdkEvent* event, bool)
2230 framecnt_t fade_length;
2232 framepos_t const pos = adjusted_current_frame (event);
2234 boost::shared_ptr<Region> region = _primary->region ();
2236 if (pos > (region->last_frame() - 64)) {
2237 fade_length = 64; // this should really be a minimum fade defined somewhere
2239 else if (pos < region->position()) {
2240 fade_length = region->length();
2243 fade_length = region->last_frame() - pos;
2246 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2248 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2254 tmp->reset_fade_out_shape_width (fade_length);
2255 tmp->show_fade_line(region->length() - fade_length);
2258 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2262 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2264 if (!movement_occurred) {
2268 framecnt_t fade_length;
2270 framepos_t const pos = adjusted_current_frame (event);
2272 boost::shared_ptr<Region> region = _primary->region ();
2274 if (pos > (region->last_frame() - 64)) {
2275 fade_length = 64; // this should really be a minimum fade defined somewhere
2277 else if (pos < region->position()) {
2278 fade_length = region->length();
2281 fade_length = region->last_frame() - pos;
2284 _editor->begin_reversible_command (_("change fade out length"));
2286 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2288 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2294 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2295 XMLNode &before = alist->get_state();
2297 tmp->audio_region()->set_fade_out_length (fade_length);
2298 tmp->audio_region()->set_fade_out_active (true);
2299 tmp->hide_fade_line();
2301 XMLNode &after = alist->get_state();
2302 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2305 _editor->commit_reversible_command ();
2309 FadeOutDrag::aborted ()
2311 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2312 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2318 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2319 tmp->hide_fade_line();
2323 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2326 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2328 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2331 _points.push_back (Gnome::Art::Point (0, 0));
2332 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2334 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2335 _line->property_width_pixels() = 1;
2336 _line->property_points () = _points;
2339 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2342 MarkerDrag::~MarkerDrag ()
2344 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2350 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2352 Drag::start_grab (event, cursor);
2356 Location *location = _editor->find_location_from_marker (_marker, is_start);
2357 _editor->_dragging_edit_point = true;
2359 update_item (location);
2361 // _drag_line->show();
2362 // _line->raise_to_top();
2365 _editor->show_verbose_time_cursor (location->start(), 10);
2367 _editor->show_verbose_time_cursor (location->end(), 10);
2370 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2373 case Selection::Toggle:
2374 _editor->selection->toggle (_marker);
2376 case Selection::Set:
2377 if (!_editor->selection->selected (_marker)) {
2378 _editor->selection->set (_marker);
2381 case Selection::Extend:
2383 Locations::LocationList ll;
2384 list<Marker*> to_add;
2386 _editor->selection->markers.range (s, e);
2387 s = min (_marker->position(), s);
2388 e = max (_marker->position(), e);
2391 if (e < max_framepos) {
2394 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2395 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2396 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2399 to_add.push_back (lm->start);
2402 to_add.push_back (lm->end);
2406 if (!to_add.empty()) {
2407 _editor->selection->add (to_add);
2411 case Selection::Add:
2412 _editor->selection->add (_marker);
2416 /* Set up copies for us to manipulate during the drag */
2418 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2419 Location* l = _editor->find_location_from_marker (*i, is_start);
2420 _copied_locations.push_back (new Location (*l));
2425 MarkerDrag::setup_pointer_frame_offset ()
2428 Location *location = _editor->find_location_from_marker (_marker, is_start);
2429 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2433 MarkerDrag::motion (GdkEvent* event, bool)
2435 framecnt_t f_delta = 0;
2437 bool move_both = false;
2439 Location *real_location;
2440 Location *copy_location = 0;
2442 framepos_t const newframe = adjusted_current_frame (event);
2444 framepos_t next = newframe;
2446 if (newframe == last_pointer_frame()) {
2450 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2454 MarkerSelection::iterator i;
2455 list<Location*>::iterator x;
2457 /* find the marker we're dragging, and compute the delta */
2459 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2460 x != _copied_locations.end() && i != _editor->selection->markers.end();
2466 if (marker == _marker) {
2468 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2473 if (real_location->is_mark()) {
2474 f_delta = newframe - copy_location->start();
2478 switch (marker->type()) {
2479 case Marker::SessionStart:
2480 case Marker::RangeStart:
2481 case Marker::LoopStart:
2482 case Marker::PunchIn:
2483 f_delta = newframe - copy_location->start();
2486 case Marker::SessionEnd:
2487 case Marker::RangeEnd:
2488 case Marker::LoopEnd:
2489 case Marker::PunchOut:
2490 f_delta = newframe - copy_location->end();
2493 /* what kind of marker is this ? */
2501 if (i == _editor->selection->markers.end()) {
2502 /* hmm, impossible - we didn't find the dragged marker */
2506 /* now move them all */
2508 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2509 x != _copied_locations.end() && i != _editor->selection->markers.end();
2515 /* call this to find out if its the start or end */
2517 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2521 if (real_location->locked()) {
2525 if (copy_location->is_mark()) {
2529 copy_location->set_start (copy_location->start() + f_delta);
2533 framepos_t new_start = copy_location->start() + f_delta;
2534 framepos_t new_end = copy_location->end() + f_delta;
2536 if (is_start) { // start-of-range marker
2539 copy_location->set_start (new_start);
2540 copy_location->set_end (new_end);
2541 } else if (new_start < copy_location->end()) {
2542 copy_location->set_start (new_start);
2544 _editor->snap_to (next, 1, true);
2545 copy_location->set_end (next);
2546 copy_location->set_start (newframe);
2549 } else { // end marker
2552 copy_location->set_end (new_end);
2553 copy_location->set_start (new_start);
2554 } else if (new_end > copy_location->start()) {
2555 copy_location->set_end (new_end);
2556 } else if (newframe > 0) {
2557 _editor->snap_to (next, -1, true);
2558 copy_location->set_start (next);
2559 copy_location->set_end (newframe);
2564 update_item (copy_location);
2566 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2569 lm->set_position (copy_location->start(), copy_location->end());
2573 assert (!_copied_locations.empty());
2575 _editor->show_verbose_time_cursor (newframe, 10);
2578 _editor->update_canvas_now ();
2583 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2585 if (!movement_occurred) {
2587 /* just a click, do nothing but finish
2588 off the selection process
2591 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2594 case Selection::Set:
2595 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2596 _editor->selection->set (_marker);
2600 case Selection::Toggle:
2601 case Selection::Extend:
2602 case Selection::Add:
2609 _editor->_dragging_edit_point = false;
2611 _editor->begin_reversible_command ( _("move marker") );
2612 XMLNode &before = _editor->session()->locations()->get_state();
2614 MarkerSelection::iterator i;
2615 list<Location*>::iterator x;
2618 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2619 x != _copied_locations.end() && i != _editor->selection->markers.end();
2622 Location * location = _editor->find_location_from_marker (*i, is_start);
2626 if (location->locked()) {
2630 if (location->is_mark()) {
2631 location->set_start ((*x)->start());
2633 location->set ((*x)->start(), (*x)->end());
2638 XMLNode &after = _editor->session()->locations()->get_state();
2639 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2640 _editor->commit_reversible_command ();
2646 MarkerDrag::aborted ()
2652 MarkerDrag::update_item (Location* location)
2654 double const x1 = _editor->frame_to_pixel (location->start());
2656 _points.front().set_x(x1);
2657 _points.back().set_x(x1);
2658 _line->property_points() = _points;
2661 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2663 _cumulative_x_drag (0),
2664 _cumulative_y_drag (0)
2666 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2668 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2674 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2676 Drag::start_grab (event, _editor->cursors()->fader);
2678 // start the grab at the center of the control point so
2679 // the point doesn't 'jump' to the mouse after the first drag
2680 _fixed_grab_x = _point->get_x();
2681 _fixed_grab_y = _point->get_y();
2683 float const fraction = 1 - (_point->get_y() / _point->line().height());
2685 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2687 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2688 event->button.x + 10, event->button.y + 10);
2690 _editor->show_verbose_canvas_cursor ();
2694 ControlPointDrag::motion (GdkEvent* event, bool)
2696 double dx = _drags->current_pointer_x() - last_pointer_x();
2697 double dy = _drags->current_pointer_y() - last_pointer_y();
2699 if (event->button.state & Keyboard::SecondaryModifier) {
2704 /* coordinate in pixels relative to the start of the region (for region-based automation)
2705 or track (for track-based automation) */
2706 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2707 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2709 // calculate zero crossing point. back off by .01 to stay on the
2710 // positive side of zero
2711 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2713 // make sure we hit zero when passing through
2714 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2718 if (_x_constrained) {
2721 if (_y_constrained) {
2725 _cumulative_x_drag = cx - _fixed_grab_x;
2726 _cumulative_y_drag = cy - _fixed_grab_y;
2730 cy = min ((double) _point->line().height(), cy);
2732 framepos_t cx_frames = _editor->unit_to_frame (cx);
2734 if (!_x_constrained) {
2735 _editor->snap_to_with_modifier (cx_frames, event);
2738 cx_frames = min (cx_frames, _point->line().maximum_time());
2740 float const fraction = 1.0 - (cy / _point->line().height());
2742 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2744 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2746 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2750 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2752 if (!movement_occurred) {
2756 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2757 _editor->reset_point_selection ();
2761 motion (event, false);
2764 _point->line().end_drag ();
2765 _editor->session()->commit_reversible_command ();
2769 ControlPointDrag::aborted ()
2771 _point->line().reset ();
2775 ControlPointDrag::active (Editing::MouseMode m)
2777 if (m == Editing::MouseGain) {
2778 /* always active in mouse gain */
2782 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2783 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2786 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2789 _cumulative_y_drag (0)
2791 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2795 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2797 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2800 _item = &_line->grab_item ();
2802 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2803 origin, and ditto for y.
2806 double cx = event->button.x;
2807 double cy = event->button.y;
2809 _line->parent_group().w2i (cx, cy);
2811 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2816 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2817 /* no adjacent points */
2821 Drag::start_grab (event, _editor->cursors()->fader);
2823 /* store grab start in parent frame */
2828 double fraction = 1.0 - (cy / _line->height());
2830 _line->start_drag_line (before, after, fraction);
2832 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2833 event->button.x + 10, event->button.y + 10);
2835 _editor->show_verbose_canvas_cursor ();
2839 LineDrag::motion (GdkEvent* event, bool)
2841 double dy = _drags->current_pointer_y() - last_pointer_y();
2843 if (event->button.state & Keyboard::SecondaryModifier) {
2847 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2849 _cumulative_y_drag = cy - _fixed_grab_y;
2852 cy = min ((double) _line->height(), cy);
2854 double const fraction = 1.0 - (cy / _line->height());
2858 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2864 /* we are ignoring x position for this drag, so we can just pass in anything */
2865 _line->drag_motion (0, fraction, true, push);
2867 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2871 LineDrag::finished (GdkEvent* event, bool)
2873 motion (event, false);
2875 _editor->session()->commit_reversible_command ();
2879 LineDrag::aborted ()
2884 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2887 _cumulative_x_drag (0)
2889 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2893 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2895 Drag::start_grab (event);
2897 _line = reinterpret_cast<SimpleLine*> (_item);
2900 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2902 double cx = event->button.x;
2903 double cy = event->button.y;
2905 _item->property_parent().get_value()->w2i(cx, cy);
2907 /* store grab start in parent frame */
2908 _region_view_grab_x = cx;
2910 _before = _line->property_x1();
2912 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2914 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2918 FeatureLineDrag::motion (GdkEvent*, bool)
2920 double dx = _drags->current_pointer_x() - last_pointer_x();
2922 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2924 _cumulative_x_drag += dx;
2926 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2935 _line->property_x1() = cx;
2936 _line->property_x2() = cx;
2938 _before = _line->property_x1();
2942 FeatureLineDrag::finished (GdkEvent*, bool)
2944 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2945 _arv->update_transient(_before, _line->property_x1());
2949 FeatureLineDrag::aborted ()
2954 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2957 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2961 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2963 Drag::start_grab (event);
2964 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2968 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2975 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2977 framepos_t grab = grab_frame ();
2978 if (Config->get_rubberbanding_snaps_to_grid ()) {
2979 _editor->snap_to_with_modifier (grab, event);
2982 /* base start and end on initial click position */
2992 if (_drags->current_pointer_y() < grab_y()) {
2993 y1 = _drags->current_pointer_y();
2996 y2 = _drags->current_pointer_y();
3001 if (start != end || y1 != y2) {
3003 double x1 = _editor->frame_to_pixel (start);
3004 double x2 = _editor->frame_to_pixel (end);
3006 _editor->rubberband_rect->property_x1() = x1;
3007 _editor->rubberband_rect->property_y1() = y1;
3008 _editor->rubberband_rect->property_x2() = x2;
3009 _editor->rubberband_rect->property_y2() = y2;
3011 _editor->rubberband_rect->show();
3012 _editor->rubberband_rect->raise_to_top();
3014 _editor->show_verbose_time_cursor (pf, 10);
3019 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3021 if (movement_occurred) {
3023 motion (event, false);
3026 if (_drags->current_pointer_y() < grab_y()) {
3027 y1 = _drags->current_pointer_y();
3030 y2 = _drags->current_pointer_y();
3035 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3038 _editor->begin_reversible_command (_("rubberband selection"));
3040 if (grab_frame() < last_pointer_frame()) {
3041 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3043 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3047 _editor->commit_reversible_command ();
3051 if (!getenv("ARDOUR_SAE")) {
3052 _editor->selection->clear_tracks();
3054 _editor->selection->clear_regions();
3055 _editor->selection->clear_points ();
3056 _editor->selection->clear_lines ();
3059 _editor->rubberband_rect->hide();
3063 RubberbandSelectDrag::aborted ()
3065 _editor->rubberband_rect->hide ();
3068 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3069 : RegionDrag (e, i, p, v)
3071 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3075 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3077 Drag::start_grab (event, cursor);
3079 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3083 TimeFXDrag::motion (GdkEvent* event, bool)
3085 RegionView* rv = _primary;
3087 framepos_t const pf = adjusted_current_frame (event);
3089 if (pf > rv->region()->position()) {
3090 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3093 _editor->show_verbose_time_cursor (pf, 10);
3097 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3099 _primary->get_time_axis_view().hide_timestretch ();
3101 if (!movement_occurred) {
3105 if (last_pointer_frame() < _primary->region()->position()) {
3106 /* backwards drag of the left edge - not usable */
3110 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3112 float percentage = (double) newlen / (double) _primary->region()->length();
3114 #ifndef USE_RUBBERBAND
3115 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3116 if (_primary->region()->data_type() == DataType::AUDIO) {
3117 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3121 _editor->begin_reversible_command (_("timestretch"));
3123 // XXX how do timeFX on multiple regions ?
3128 if (_editor->time_stretch (rs, percentage) == -1) {
3129 error << _("An error occurred while executing time stretch operation") << endmsg;
3134 TimeFXDrag::aborted ()
3136 _primary->get_time_axis_view().hide_timestretch ();
3139 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3142 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3146 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3148 Drag::start_grab (event);
3152 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3154 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3158 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3160 if (movement_occurred && _editor->session()) {
3161 /* make sure we stop */
3162 _editor->session()->request_transport_speed (0.0);
3167 ScrubDrag::aborted ()
3172 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3176 , _original_pointer_time_axis (-1)
3177 , _last_pointer_time_axis (-1)
3179 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3183 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3185 if (_editor->session() == 0) {
3189 Gdk::Cursor* cursor = 0;
3191 switch (_operation) {
3192 case CreateSelection:
3193 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3198 cursor = _editor->cursors()->selector;
3199 Drag::start_grab (event, cursor);
3202 case SelectionStartTrim:
3203 if (_editor->clicked_axisview) {
3204 _editor->clicked_axisview->order_selection_trims (_item, true);
3206 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3209 case SelectionEndTrim:
3210 if (_editor->clicked_axisview) {
3211 _editor->clicked_axisview->order_selection_trims (_item, false);
3213 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3217 Drag::start_grab (event, cursor);
3221 if (_operation == SelectionMove) {
3222 _editor->show_verbose_time_cursor (_editor->selection->time[_editor->clicked_selection].start, 10);
3224 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3227 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3231 SelectionDrag::setup_pointer_frame_offset ()
3233 switch (_operation) {
3234 case CreateSelection:
3235 _pointer_frame_offset = 0;
3238 case SelectionStartTrim:
3240 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3243 case SelectionEndTrim:
3244 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3250 SelectionDrag::motion (GdkEvent* event, bool first_move)
3252 framepos_t start = 0;
3256 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3257 if (pending_time_axis.first == 0) {
3261 framepos_t const pending_position = adjusted_current_frame (event);
3263 /* only alter selection if things have changed */
3265 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3269 switch (_operation) {
3270 case CreateSelection:
3272 framepos_t grab = grab_frame ();
3275 _editor->snap_to (grab);
3278 if (pending_position < grab_frame()) {
3279 start = pending_position;
3282 end = pending_position;
3286 /* first drag: Either add to the selection
3287 or create a new selection
3293 /* adding to the selection */
3294 _editor->set_selected_track_as_side_effect (Selection::Add);
3295 //_editor->selection->add (_editor->clicked_axisview);
3296 _editor->clicked_selection = _editor->selection->add (start, end);
3301 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3302 //_editor->selection->set (_editor->clicked_axisview);
3303 _editor->set_selected_track_as_side_effect (Selection::Set);
3306 _editor->clicked_selection = _editor->selection->set (start, end);
3310 /* select the track that we're in */
3311 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3312 // _editor->set_selected_track_as_side_effect (Selection::Add);
3313 _editor->selection->add (pending_time_axis.first);
3314 _added_time_axes.push_back (pending_time_axis.first);
3317 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3318 tracks that we selected in the first place.
3321 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3322 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3324 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3325 while (i != _added_time_axes.end()) {
3327 list<TimeAxisView*>::iterator tmp = i;
3330 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3331 _editor->selection->remove (*i);
3332 _added_time_axes.remove (*i);
3341 case SelectionStartTrim:
3343 start = _editor->selection->time[_editor->clicked_selection].start;
3344 end = _editor->selection->time[_editor->clicked_selection].end;
3346 if (pending_position > end) {
3349 start = pending_position;
3353 case SelectionEndTrim:
3355 start = _editor->selection->time[_editor->clicked_selection].start;
3356 end = _editor->selection->time[_editor->clicked_selection].end;
3358 if (pending_position < start) {
3361 end = pending_position;
3368 start = _editor->selection->time[_editor->clicked_selection].start;
3369 end = _editor->selection->time[_editor->clicked_selection].end;
3371 length = end - start;
3373 start = pending_position;
3374 _editor->snap_to (start);
3376 end = start + length;
3381 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3382 _editor->start_canvas_autoscroll (1, 0);
3386 _editor->selection->replace (_editor->clicked_selection, start, end);
3389 if (_operation == SelectionMove) {
3390 _editor->show_verbose_time_cursor(start, 10);
3392 _editor->show_verbose_time_cursor(pending_position, 10);
3397 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3399 Session* s = _editor->session();
3401 if (movement_occurred) {
3402 motion (event, false);
3403 /* XXX this is not object-oriented programming at all. ick */
3404 if (_editor->selection->time.consolidate()) {
3405 _editor->selection->TimeChanged ();
3408 /* XXX what if its a music time selection? */
3409 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3410 s->request_play_range (&_editor->selection->time, true);
3415 /* just a click, no pointer movement.*/
3417 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3418 _editor->selection->clear_time();
3421 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3422 _editor->selection->set (_editor->clicked_axisview);
3425 if (s && s->get_play_range () && s->transport_rolling()) {
3426 s->request_stop (false, false);
3431 _editor->stop_canvas_autoscroll ();
3435 SelectionDrag::aborted ()
3440 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3445 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3447 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3448 physical_screen_height (_editor->get_window()));
3449 _drag_rect->hide ();
3451 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3452 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3456 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3458 if (_editor->session() == 0) {
3462 Gdk::Cursor* cursor = 0;
3464 if (!_editor->temp_location) {
3465 _editor->temp_location = new Location (*_editor->session());
3468 switch (_operation) {
3469 case CreateRangeMarker:
3470 case CreateTransportMarker:
3471 case CreateCDMarker:
3473 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3478 cursor = _editor->cursors()->selector;
3482 Drag::start_grab (event, cursor);
3484 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3488 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3490 framepos_t start = 0;
3492 ArdourCanvas::SimpleRect *crect;
3494 switch (_operation) {
3495 case CreateRangeMarker:
3496 crect = _editor->range_bar_drag_rect;
3498 case CreateTransportMarker:
3499 crect = _editor->transport_bar_drag_rect;
3501 case CreateCDMarker:
3502 crect = _editor->cd_marker_bar_drag_rect;
3505 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3510 framepos_t const pf = adjusted_current_frame (event);
3512 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3513 framepos_t grab = grab_frame ();
3514 _editor->snap_to (grab);
3516 if (pf < grab_frame()) {
3524 /* first drag: Either add to the selection
3525 or create a new selection.
3530 _editor->temp_location->set (start, end);
3534 update_item (_editor->temp_location);
3536 //_drag_rect->raise_to_top();
3541 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3542 _editor->start_canvas_autoscroll (1, 0);
3546 _editor->temp_location->set (start, end);
3548 double x1 = _editor->frame_to_pixel (start);
3549 double x2 = _editor->frame_to_pixel (end);
3550 crect->property_x1() = x1;
3551 crect->property_x2() = x2;
3553 update_item (_editor->temp_location);
3556 _editor->show_verbose_time_cursor (pf, 10);
3561 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3563 Location * newloc = 0;
3567 if (movement_occurred) {
3568 motion (event, false);
3571 switch (_operation) {
3572 case CreateRangeMarker:
3573 case CreateCDMarker:
3575 _editor->begin_reversible_command (_("new range marker"));
3576 XMLNode &before = _editor->session()->locations()->get_state();
3577 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3578 if (_operation == CreateCDMarker) {
3579 flags = Location::IsRangeMarker | Location::IsCDMarker;
3580 _editor->cd_marker_bar_drag_rect->hide();
3583 flags = Location::IsRangeMarker;
3584 _editor->range_bar_drag_rect->hide();
3586 newloc = new Location (
3587 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3590 _editor->session()->locations()->add (newloc, true);
3591 XMLNode &after = _editor->session()->locations()->get_state();
3592 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3593 _editor->commit_reversible_command ();
3597 case CreateTransportMarker:
3598 // popup menu to pick loop or punch
3599 _editor->new_transport_marker_context_menu (&event->button, _item);
3603 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3605 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3610 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3612 if (end == max_framepos) {
3613 end = _editor->session()->current_end_frame ();
3616 if (start == max_framepos) {
3617 start = _editor->session()->current_start_frame ();
3620 switch (_editor->mouse_mode) {
3622 /* find the two markers on either side and then make the selection from it */
3623 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3627 /* find the two markers on either side of the click and make the range out of it */
3628 _editor->selection->set (start, end);
3637 _editor->stop_canvas_autoscroll ();
3641 RangeMarkerBarDrag::aborted ()
3647 RangeMarkerBarDrag::update_item (Location* location)
3649 double const x1 = _editor->frame_to_pixel (location->start());
3650 double const x2 = _editor->frame_to_pixel (location->end());
3652 _drag_rect->property_x1() = x1;
3653 _drag_rect->property_x2() = x2;
3656 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3660 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3664 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3666 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3667 Drag::start_grab (event, _editor->cursors()->zoom_out);
3670 Drag::start_grab (event, _editor->cursors()->zoom_in);
3674 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3678 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3683 framepos_t const pf = adjusted_current_frame (event);
3685 framepos_t grab = grab_frame ();
3686 _editor->snap_to_with_modifier (grab, event);
3688 /* base start and end on initial click position */
3700 _editor->zoom_rect->show();
3701 _editor->zoom_rect->raise_to_top();
3704 _editor->reposition_zoom_rect(start, end);
3706 _editor->show_verbose_time_cursor (pf, 10);
3711 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3713 if (movement_occurred) {
3714 motion (event, false);
3716 if (grab_frame() < last_pointer_frame()) {
3717 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3719 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3722 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3725 _editor->zoom_rect->hide();
3729 MouseZoomDrag::aborted ()
3731 _editor->zoom_rect->hide ();
3734 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3736 , _cumulative_dx (0)
3737 , _cumulative_dy (0)
3739 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3741 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3742 _region = &_primary->region_view ();
3743 _note_height = _region->midi_stream_view()->note_height ();
3747 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3749 Drag::start_grab (event);
3751 if (!(_was_selected = _primary->selected())) {
3753 /* tertiary-click means extend selection - we'll do that on button release,
3754 so don't add it here, because otherwise we make it hard to figure
3755 out the "extend-to" range.
3758 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3761 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3764 _region->note_selected (_primary, true);
3766 _region->unique_select (_primary);
3772 /** @return Current total drag x change in frames */
3774 NoteDrag::total_dx () const
3777 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3779 /* primary note time */
3780 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3782 /* new time of the primary note relative to the region position */
3783 frameoffset_t const st = n + dx;
3785 /* snap and return corresponding delta */
3786 return _region->snap_frame_to_frame (st) - n;
3789 /** @return Current total drag y change in notes */
3791 NoteDrag::total_dy () const
3793 /* this is `backwards' to make increasing note number go in the right direction */
3794 double const dy = _drags->current_pointer_y() - grab_y();
3799 if (abs (dy) >= _note_height) {
3801 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3803 ndy = (int8_t) floor (dy / _note_height / 2.0);
3807 /* more positive value = higher pitch and higher y-axis position on track,
3808 which is the inverse of the X-centric geometric universe
3815 NoteDrag::motion (GdkEvent *, bool)
3817 /* Total change in x and y since the start of the drag */
3818 frameoffset_t const dx = total_dx ();
3819 int8_t const dy = -total_dy ();
3821 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3822 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3823 double const tdy = dy * _note_height - _cumulative_dy;
3826 _cumulative_dx += tdx;
3827 _cumulative_dy += tdy;
3829 int8_t note_delta = total_dy();
3831 _region->move_selection (tdx, tdy, note_delta);
3834 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3835 (int) floor (_primary->note()->note() + note_delta));
3837 _editor->show_verbose_canvas_cursor_with (buf);
3842 NoteDrag::finished (GdkEvent* ev, bool moved)
3845 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3847 if (_was_selected) {
3848 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3850 _region->note_deselected (_primary);
3853 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3854 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3856 if (!extend && !add && _region->selection_size() > 1) {
3857 _region->unique_select (_primary);
3858 } else if (extend) {
3859 _region->note_selected (_primary, true, true);
3861 /* it was added during button press */
3866 _region->note_dropped (_primary, total_dx(), total_dy());
3871 NoteDrag::aborted ()
3876 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3877 : Drag (editor, item)
3879 , _nothing_to_drag (false)
3881 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3883 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3886 /* get all lines in the automation view */
3887 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3889 /* find those that overlap the ranges being dragged */
3890 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3891 while (i != lines.end ()) {
3892 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3895 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3897 /* check this range against all the AudioRanges that we are using */
3898 list<AudioRange>::const_iterator k = _ranges.begin ();
3899 while (k != _ranges.end()) {
3900 if (k->coverage (r.first, r.second) != OverlapNone) {
3906 /* add it to our list if it overlaps at all */
3907 if (k != _ranges.end()) {
3912 _lines.push_back (n);
3918 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3922 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3924 Drag::start_grab (event, cursor);
3926 /* Get line states before we start changing things */
3927 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3928 i->state = &i->line->get_state ();
3931 if (_ranges.empty()) {
3933 /* No selected time ranges: drag all points */
3934 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3935 uint32_t const N = i->line->npoints ();
3936 for (uint32_t j = 0; j < N; ++j) {
3937 i->points.push_back (i->line->nth (j));
3943 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3945 framecnt_t const half = (i->start + i->end) / 2;
3947 /* find the line that this audio range starts in */
3948 list<Line>::iterator j = _lines.begin();
3949 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3953 if (j != _lines.end()) {
3954 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3956 /* j is the line that this audio range starts in; fade into it;
3957 64 samples length plucked out of thin air.
3960 framepos_t a = i->start + 64;
3965 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3966 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3968 the_list->add (p, the_list->eval (p));
3969 j->line->add_always_in_view (p);
3970 the_list->add (q, the_list->eval (q));
3971 j->line->add_always_in_view (q);
3974 /* same thing for the end */
3977 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3981 if (j != _lines.end()) {
3982 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3984 /* j is the line that this audio range starts in; fade out of it;
3985 64 samples length plucked out of thin air.
3988 framepos_t b = i->end - 64;
3993 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3994 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
3996 the_list->add (p, the_list->eval (p));
3997 j->line->add_always_in_view (p);
3998 the_list->add (q, the_list->eval (q));
3999 j->line->add_always_in_view (q);
4003 _nothing_to_drag = true;
4005 /* Find all the points that should be dragged and put them in the relevant
4006 points lists in the Line structs.
4009 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4011 uint32_t const N = i->line->npoints ();
4012 for (uint32_t j = 0; j < N; ++j) {
4014 /* here's a control point on this line */
4015 ControlPoint* p = i->line->nth (j);
4016 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4018 /* see if it's inside a range */
4019 list<AudioRange>::const_iterator k = _ranges.begin ();
4020 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4024 if (k != _ranges.end()) {
4025 /* dragging this point */
4026 _nothing_to_drag = false;
4027 i->points.push_back (p);
4033 if (_nothing_to_drag) {
4037 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4038 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4043 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4045 if (_nothing_to_drag) {
4049 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4050 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4052 /* we are ignoring x position for this drag, so we can just pass in anything */
4053 i->line->drag_motion (0, f, true, false);
4058 AutomationRangeDrag::finished (GdkEvent* event, bool)
4060 if (_nothing_to_drag) {
4064 motion (event, false);
4065 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4066 i->line->end_drag ();
4067 i->line->clear_always_in_view ();
4070 _editor->session()->commit_reversible_command ();
4074 AutomationRangeDrag::aborted ()
4076 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4077 i->line->clear_always_in_view ();
4082 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4085 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4086 layer = v->region()->layer ();
4087 initial_y = v->get_canvas_group()->property_y ();
4088 initial_playlist = v->region()->playlist ();
4089 initial_position = v->region()->position ();
4090 initial_end = v->region()->position () + v->region()->length ();
4093 ProgramChangeDrag::ProgramChangeDrag (Editor* e, CanvasProgramChange* i, MidiRegionView* r)
4096 , _program_change (i)
4097 , _cumulative_dx (0)
4099 DEBUG_TRACE (DEBUG::Drags, "New ProgramChangeDrag\n");
4103 ProgramChangeDrag::motion (GdkEvent* ev, bool)
4105 framepos_t f = adjusted_current_frame (ev);
4106 boost::shared_ptr<Region> r = _region_view->region ();
4107 f = max (f, r->position ());
4108 f = min (f, r->last_frame ());
4110 framecnt_t const dxf = f - grab_frame();
4111 double const dxu = _editor->frame_to_unit (dxf);
4112 _program_change->move (dxu - _cumulative_dx, 0);
4113 _cumulative_dx = dxu;
4117 ProgramChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4119 if (!movement_occurred) {
4123 boost::shared_ptr<Region> r (_region_view->region ());
4125 framepos_t f = adjusted_current_frame (ev);
4126 f = max (f, r->position ());
4127 f = min (f, r->last_frame ());
4129 _region_view->move_program_change (
4130 MidiRegionView::PCEvent (_program_change->event_time(), _program_change->program(), _program_change->channel()),
4131 _region_view->frames_to_beats (f - r->position() - r->start())
4136 ProgramChangeDrag::aborted ()
4138 _program_change->move (-_cumulative_dx, 0);
4142 ProgramChangeDrag::setup_pointer_frame_offset ()
4144 boost::shared_ptr<Region> region = _region_view->region ();
4145 _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_program_change->event_time()) - region->position() + region->start();