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 () */
342 aborted (_move_threshold_passed);
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 (bool movement_occurred)
1140 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1147 RegionMotionDrag::aborted (movement_occurred);
1152 RegionMotionDrag::aborted (bool)
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 (bool)
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 (bool)
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 (bool)
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 (bool)
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 (bool)
1511 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1512 : RegionDrag (e, i, p, v)
1514 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1518 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1521 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1522 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1524 if (tv && tv->is_track()) {
1525 speed = tv->track()->speed();
1528 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1529 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1530 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1532 framepos_t const pf = adjusted_current_frame (event);
1534 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1535 /* Move the contents of the region around without changing the region bounds */
1536 _operation = ContentsTrim;
1537 Drag::start_grab (event, _editor->cursors()->trimmer);
1539 /* These will get overridden for a point trim.*/
1540 if (pf < (region_start + region_length/2)) {
1541 /* closer to front */
1542 _operation = StartTrim;
1543 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1546 _operation = EndTrim;
1547 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1551 switch (_operation) {
1553 _editor->show_verbose_time_cursor (region_start, 10);
1554 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1555 i->view->trim_front_starting ();
1559 _editor->show_verbose_time_cursor (region_end, 10);
1562 _editor->show_verbose_time_cursor (pf, 10);
1566 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1567 i->view->region()->suspend_property_changes ();
1572 TrimDrag::motion (GdkEvent* event, bool first_move)
1574 RegionView* rv = _primary;
1577 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1578 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1579 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1581 if (tv && tv->is_track()) {
1582 speed = tv->track()->speed();
1585 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1591 switch (_operation) {
1593 trim_type = "Region start trim";
1596 trim_type = "Region end trim";
1599 trim_type = "Region content trim";
1603 _editor->begin_reversible_command (trim_type);
1605 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1606 RegionView* rv = i->view;
1607 rv->fake_set_opaque (false);
1608 rv->enable_display (false);
1609 rv->region()->playlist()->clear_owned_changes ();
1611 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1614 arv->temporarily_hide_envelope ();
1617 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1618 insert_result = _editor->motion_frozen_playlists.insert (pl);
1620 if (insert_result.second) {
1626 bool non_overlap_trim = false;
1628 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1629 non_overlap_trim = true;
1632 switch (_operation) {
1634 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1635 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1640 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1641 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1647 bool swap_direction = false;
1649 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1650 swap_direction = true;
1653 framecnt_t frame_delta = 0;
1655 bool left_direction = false;
1656 if (last_pointer_frame() > adjusted_current_frame(event)) {
1657 left_direction = true;
1660 if (left_direction) {
1661 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1663 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1666 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1667 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1673 switch (_operation) {
1675 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->position() / speed), 10);
1678 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->last_frame() / speed), 10);
1681 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1688 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1690 if (movement_occurred) {
1691 motion (event, false);
1693 /* This must happen before the region's StatefulDiffCommand is created, as it may
1694 `correct' (ahem) the region's _start from being negative to being zero. It
1695 needs to be zero in the undo record.
1697 if (_operation == StartTrim) {
1698 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1699 i->view->trim_front_ending ();
1703 if (!_editor->selection->selected (_primary)) {
1704 _primary->thaw_after_trim ();
1707 set<boost::shared_ptr<Playlist> > diffed_playlists;
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);
1714 /* Trimming one region may affect others on the playlist, so we need
1715 to get undo Commands from the whole playlist rather than just the
1716 region. Use diffed_playlists to make sure we don't diff a given
1717 playlist more than once.
1719 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1720 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1721 vector<Command*> cmds;
1723 _editor->session()->add_commands (cmds);
1724 diffed_playlists.insert (p);
1728 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1732 _editor->motion_frozen_playlists.clear ();
1733 _editor->commit_reversible_command();
1736 /* no mouse movement */
1737 _editor->point_trim (event, adjusted_current_frame (event));
1740 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1741 if (_operation == StartTrim) {
1742 i->view->trim_front_ending ();
1745 i->view->region()->resume_property_changes ();
1750 TrimDrag::aborted (bool movement_occurred)
1752 /* Our motion method is changing model state, so use the Undo system
1753 to cancel. Perhaps not ideal, as this will leave an Undo point
1754 behind which may be slightly odd from the user's point of view.
1759 if (movement_occurred) {
1763 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1764 i->view->region()->resume_property_changes ();
1769 TrimDrag::setup_pointer_frame_offset ()
1771 list<DraggingView>::iterator i = _views.begin ();
1772 while (i != _views.end() && i->view != _primary) {
1776 if (i == _views.end()) {
1780 switch (_operation) {
1782 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1785 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1792 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1796 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1798 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1803 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1806 // create a dummy marker for visual representation of moving the copy.
1807 // The actual copying is not done before we reach the finish callback.
1809 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1811 MeterMarker* new_marker = new MeterMarker (
1813 *_editor->meter_group,
1814 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1816 *new MeterSection (_marker->meter())
1819 _item = &new_marker->the_item ();
1820 _marker = new_marker;
1824 MetricSection& section (_marker->meter());
1826 if (!section.movable()) {
1832 Drag::start_grab (event, cursor);
1834 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1838 MeterMarkerDrag::setup_pointer_frame_offset ()
1840 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1844 MeterMarkerDrag::motion (GdkEvent* event, bool)
1846 framepos_t const pf = adjusted_current_frame (event);
1848 _marker->set_position (pf);
1850 _editor->show_verbose_time_cursor (pf, 10);
1854 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1856 if (!movement_occurred) {
1860 motion (event, false);
1862 Timecode::BBT_Time when;
1864 TempoMap& map (_editor->session()->tempo_map());
1865 map.bbt_time (last_pointer_frame(), when);
1867 if (_copy == true) {
1868 _editor->begin_reversible_command (_("copy meter mark"));
1869 XMLNode &before = map.get_state();
1870 map.add_meter (_marker->meter(), when);
1871 XMLNode &after = map.get_state();
1872 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1873 _editor->commit_reversible_command ();
1875 // delete the dummy marker we used for visual representation of copying.
1876 // a new visual marker will show up automatically.
1879 _editor->begin_reversible_command (_("move meter mark"));
1880 XMLNode &before = map.get_state();
1881 map.move_meter (_marker->meter(), when);
1882 XMLNode &after = map.get_state();
1883 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1884 _editor->commit_reversible_command ();
1889 MeterMarkerDrag::aborted (bool)
1891 _marker->set_position (_marker->meter().frame ());
1894 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1898 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1900 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1905 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1909 // create a dummy marker for visual representation of moving the copy.
1910 // The actual copying is not done before we reach the finish callback.
1912 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1914 TempoMarker* new_marker = new TempoMarker (
1916 *_editor->tempo_group,
1917 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1919 *new TempoSection (_marker->tempo())
1922 _item = &new_marker->the_item ();
1923 _marker = new_marker;
1927 Drag::start_grab (event, cursor);
1929 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1933 TempoMarkerDrag::setup_pointer_frame_offset ()
1935 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1939 TempoMarkerDrag::motion (GdkEvent* event, bool)
1941 framepos_t const pf = adjusted_current_frame (event);
1942 _marker->set_position (pf);
1943 _editor->show_verbose_time_cursor (pf, 10);
1947 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1949 if (!movement_occurred) {
1953 motion (event, false);
1955 Timecode::BBT_Time when;
1957 TempoMap& map (_editor->session()->tempo_map());
1958 map.bbt_time (last_pointer_frame(), when);
1960 if (_copy == true) {
1961 _editor->begin_reversible_command (_("copy tempo mark"));
1962 XMLNode &before = map.get_state();
1963 map.add_tempo (_marker->tempo(), when);
1964 XMLNode &after = map.get_state();
1965 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1966 _editor->commit_reversible_command ();
1968 // delete the dummy marker we used for visual representation of copying.
1969 // a new visual marker will show up automatically.
1972 _editor->begin_reversible_command (_("move tempo mark"));
1973 XMLNode &before = map.get_state();
1974 map.move_tempo (_marker->tempo(), when);
1975 XMLNode &after = map.get_state();
1976 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1977 _editor->commit_reversible_command ();
1982 TempoMarkerDrag::aborted (bool)
1984 _marker->set_position (_marker->tempo().frame());
1987 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1991 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
1994 /** Do all the things we do when dragging the playhead to make it look as though
1995 * we have located, without actually doing the locate (because that would cause
1996 * the diskstream buffers to be refilled, which is too slow).
1999 CursorDrag::fake_locate (framepos_t t)
2001 _editor->playhead_cursor->set_position (t);
2003 Session* s = _editor->session ();
2004 if (s->timecode_transmission_suspended ()) {
2005 framepos_t const f = _editor->playhead_cursor->current_frame;
2006 s->send_mmc_locate (f);
2007 s->send_full_time_code (f);
2010 _editor->show_verbose_time_cursor (t, 10);
2011 _editor->UpdateAllTransportClocks (t);
2015 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2017 Drag::start_grab (event, c);
2019 framepos_t where = _editor->event_frame (event, 0, 0);
2020 _editor->snap_to_with_modifier (where, event);
2022 _editor->_dragging_playhead = true;
2024 Session* s = _editor->session ();
2027 if (_was_rolling && _stop) {
2031 if (s->is_auditioning()) {
2032 s->cancel_audition ();
2035 s->request_suspend_timecode_transmission ();
2036 while (!s->timecode_transmission_suspended ()) {
2037 /* twiddle our thumbs */
2041 fake_locate (where);
2045 CursorDrag::motion (GdkEvent* event, bool)
2047 framepos_t const adjusted_frame = adjusted_current_frame (event);
2049 if (adjusted_frame == last_pointer_frame()) {
2053 fake_locate (adjusted_frame);
2056 _editor->update_canvas_now ();
2061 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2063 _editor->_dragging_playhead = false;
2065 if (!movement_occurred && _stop) {
2069 motion (event, false);
2071 Session* s = _editor->session ();
2073 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2074 _editor->_pending_locate_request = true;
2075 s->request_resume_timecode_transmission ();
2080 CursorDrag::aborted (bool)
2082 if (_editor->_dragging_playhead) {
2083 _editor->session()->request_resume_timecode_transmission ();
2084 _editor->_dragging_playhead = false;
2087 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2090 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2091 : RegionDrag (e, i, p, v)
2093 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2097 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2099 Drag::start_grab (event, cursor);
2101 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2102 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2104 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2106 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2110 FadeInDrag::setup_pointer_frame_offset ()
2112 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2113 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2114 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2118 FadeInDrag::motion (GdkEvent* event, bool)
2120 framecnt_t fade_length;
2122 framepos_t const pos = adjusted_current_frame (event);
2124 boost::shared_ptr<Region> region = _primary->region ();
2126 if (pos < (region->position() + 64)) {
2127 fade_length = 64; // this should be a minimum defined somewhere
2128 } else if (pos > region->last_frame()) {
2129 fade_length = region->length();
2131 fade_length = pos - region->position();
2134 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2136 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2142 tmp->reset_fade_in_shape_width (fade_length);
2143 tmp->show_fade_line((framecnt_t) fade_length);
2146 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2150 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2152 if (!movement_occurred) {
2156 framecnt_t fade_length;
2158 framepos_t const pos = adjusted_current_frame (event);
2160 boost::shared_ptr<Region> region = _primary->region ();
2162 if (pos < (region->position() + 64)) {
2163 fade_length = 64; // this should be a minimum defined somewhere
2164 } else if (pos > region->last_frame()) {
2165 fade_length = region->length();
2167 fade_length = pos - region->position();
2170 _editor->begin_reversible_command (_("change fade in length"));
2172 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2174 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2180 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2181 XMLNode &before = alist->get_state();
2183 tmp->audio_region()->set_fade_in_length (fade_length);
2184 tmp->audio_region()->set_fade_in_active (true);
2185 tmp->hide_fade_line();
2187 XMLNode &after = alist->get_state();
2188 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2191 _editor->commit_reversible_command ();
2195 FadeInDrag::aborted (bool)
2197 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2198 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2204 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2205 tmp->hide_fade_line();
2209 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2210 : RegionDrag (e, i, p, v)
2212 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2216 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2218 Drag::start_grab (event, cursor);
2220 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2221 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2223 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2225 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2229 FadeOutDrag::setup_pointer_frame_offset ()
2231 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2232 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2233 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2237 FadeOutDrag::motion (GdkEvent* event, bool)
2239 framecnt_t fade_length;
2241 framepos_t const pos = adjusted_current_frame (event);
2243 boost::shared_ptr<Region> region = _primary->region ();
2245 if (pos > (region->last_frame() - 64)) {
2246 fade_length = 64; // this should really be a minimum fade defined somewhere
2248 else if (pos < region->position()) {
2249 fade_length = region->length();
2252 fade_length = region->last_frame() - pos;
2255 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2257 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2263 tmp->reset_fade_out_shape_width (fade_length);
2264 tmp->show_fade_line(region->length() - fade_length);
2267 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2271 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2273 if (!movement_occurred) {
2277 framecnt_t fade_length;
2279 framepos_t const pos = adjusted_current_frame (event);
2281 boost::shared_ptr<Region> region = _primary->region ();
2283 if (pos > (region->last_frame() - 64)) {
2284 fade_length = 64; // this should really be a minimum fade defined somewhere
2286 else if (pos < region->position()) {
2287 fade_length = region->length();
2290 fade_length = region->last_frame() - pos;
2293 _editor->begin_reversible_command (_("change fade out length"));
2295 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2297 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2303 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2304 XMLNode &before = alist->get_state();
2306 tmp->audio_region()->set_fade_out_length (fade_length);
2307 tmp->audio_region()->set_fade_out_active (true);
2308 tmp->hide_fade_line();
2310 XMLNode &after = alist->get_state();
2311 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2314 _editor->commit_reversible_command ();
2318 FadeOutDrag::aborted (bool)
2320 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2321 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2327 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2328 tmp->hide_fade_line();
2332 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2335 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2337 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2340 _points.push_back (Gnome::Art::Point (0, 0));
2341 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2344 MarkerDrag::~MarkerDrag ()
2346 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2352 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2354 Drag::start_grab (event, cursor);
2358 Location *location = _editor->find_location_from_marker (_marker, is_start);
2359 _editor->_dragging_edit_point = true;
2361 update_item (location);
2363 // _drag_line->show();
2364 // _line->raise_to_top();
2367 _editor->show_verbose_time_cursor (location->start(), 10);
2369 _editor->show_verbose_time_cursor (location->end(), 10);
2372 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2375 case Selection::Toggle:
2376 _editor->selection->toggle (_marker);
2378 case Selection::Set:
2379 if (!_editor->selection->selected (_marker)) {
2380 _editor->selection->set (_marker);
2383 case Selection::Extend:
2385 Locations::LocationList ll;
2386 list<Marker*> to_add;
2388 _editor->selection->markers.range (s, e);
2389 s = min (_marker->position(), s);
2390 e = max (_marker->position(), e);
2393 if (e < max_framepos) {
2396 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2397 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2398 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2401 to_add.push_back (lm->start);
2404 to_add.push_back (lm->end);
2408 if (!to_add.empty()) {
2409 _editor->selection->add (to_add);
2413 case Selection::Add:
2414 _editor->selection->add (_marker);
2418 /* Set up copies for us to manipulate during the drag */
2420 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2421 Location* l = _editor->find_location_from_marker (*i, is_start);
2422 _copied_locations.push_back (new Location (*l));
2427 MarkerDrag::setup_pointer_frame_offset ()
2430 Location *location = _editor->find_location_from_marker (_marker, is_start);
2431 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2435 MarkerDrag::motion (GdkEvent* event, bool)
2437 framecnt_t f_delta = 0;
2439 bool move_both = false;
2441 Location *real_location;
2442 Location *copy_location = 0;
2444 framepos_t const newframe = adjusted_current_frame (event);
2446 framepos_t next = newframe;
2448 if (newframe == last_pointer_frame()) {
2452 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2456 MarkerSelection::iterator i;
2457 list<Location*>::iterator x;
2459 /* find the marker we're dragging, and compute the delta */
2461 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2462 x != _copied_locations.end() && i != _editor->selection->markers.end();
2468 if (marker == _marker) {
2470 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2475 if (real_location->is_mark()) {
2476 f_delta = newframe - copy_location->start();
2480 switch (marker->type()) {
2481 case Marker::SessionStart:
2482 case Marker::RangeStart:
2483 case Marker::LoopStart:
2484 case Marker::PunchIn:
2485 f_delta = newframe - copy_location->start();
2488 case Marker::SessionEnd:
2489 case Marker::RangeEnd:
2490 case Marker::LoopEnd:
2491 case Marker::PunchOut:
2492 f_delta = newframe - copy_location->end();
2495 /* what kind of marker is this ? */
2503 if (i == _editor->selection->markers.end()) {
2504 /* hmm, impossible - we didn't find the dragged marker */
2508 /* now move them all */
2510 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2511 x != _copied_locations.end() && i != _editor->selection->markers.end();
2517 /* call this to find out if its the start or end */
2519 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2523 if (real_location->locked()) {
2527 if (copy_location->is_mark()) {
2531 copy_location->set_start (copy_location->start() + f_delta);
2535 framepos_t new_start = copy_location->start() + f_delta;
2536 framepos_t new_end = copy_location->end() + f_delta;
2538 if (is_start) { // start-of-range marker
2541 copy_location->set_start (new_start);
2542 copy_location->set_end (new_end);
2543 } else if (new_start < copy_location->end()) {
2544 copy_location->set_start (new_start);
2545 } else if (newframe > 0) {
2546 _editor->snap_to (next, 1, true);
2547 copy_location->set_end (next);
2548 copy_location->set_start (newframe);
2551 } else { // end marker
2554 copy_location->set_end (new_end);
2555 copy_location->set_start (new_start);
2556 } else if (new_end > copy_location->start()) {
2557 copy_location->set_end (new_end);
2558 } else if (newframe > 0) {
2559 _editor->snap_to (next, -1, true);
2560 copy_location->set_start (next);
2561 copy_location->set_end (newframe);
2566 update_item (copy_location);
2568 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2571 lm->set_position (copy_location->start(), copy_location->end());
2575 assert (!_copied_locations.empty());
2577 _editor->show_verbose_time_cursor (newframe, 10);
2580 _editor->update_canvas_now ();
2585 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2587 if (!movement_occurred) {
2589 /* just a click, do nothing but finish
2590 off the selection process
2593 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2596 case Selection::Set:
2597 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2598 _editor->selection->set (_marker);
2602 case Selection::Toggle:
2603 case Selection::Extend:
2604 case Selection::Add:
2611 _editor->_dragging_edit_point = false;
2613 _editor->begin_reversible_command ( _("move marker") );
2614 XMLNode &before = _editor->session()->locations()->get_state();
2616 MarkerSelection::iterator i;
2617 list<Location*>::iterator x;
2620 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2621 x != _copied_locations.end() && i != _editor->selection->markers.end();
2624 Location * location = _editor->find_location_from_marker (*i, is_start);
2628 if (location->locked()) {
2632 if (location->is_mark()) {
2633 location->set_start ((*x)->start());
2635 location->set ((*x)->start(), (*x)->end());
2640 XMLNode &after = _editor->session()->locations()->get_state();
2641 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2642 _editor->commit_reversible_command ();
2646 MarkerDrag::aborted (bool)
2652 MarkerDrag::update_item (Location* location)
2657 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2659 _cumulative_x_drag (0),
2660 _cumulative_y_drag (0)
2662 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2664 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2670 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2672 Drag::start_grab (event, _editor->cursors()->fader);
2674 // start the grab at the center of the control point so
2675 // the point doesn't 'jump' to the mouse after the first drag
2676 _fixed_grab_x = _point->get_x();
2677 _fixed_grab_y = _point->get_y();
2679 float const fraction = 1 - (_point->get_y() / _point->line().height());
2681 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2683 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2684 event->button.x + 10, event->button.y + 10);
2686 _editor->show_verbose_canvas_cursor ();
2690 ControlPointDrag::motion (GdkEvent* event, bool)
2692 double dx = _drags->current_pointer_x() - last_pointer_x();
2693 double dy = _drags->current_pointer_y() - last_pointer_y();
2695 if (event->button.state & Keyboard::SecondaryModifier) {
2700 /* coordinate in pixels relative to the start of the region (for region-based automation)
2701 or track (for track-based automation) */
2702 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2703 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2705 // calculate zero crossing point. back off by .01 to stay on the
2706 // positive side of zero
2707 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2709 // make sure we hit zero when passing through
2710 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2714 if (_x_constrained) {
2717 if (_y_constrained) {
2721 _cumulative_x_drag = cx - _fixed_grab_x;
2722 _cumulative_y_drag = cy - _fixed_grab_y;
2726 cy = min ((double) _point->line().height(), cy);
2728 framepos_t cx_frames = _editor->unit_to_frame (cx);
2730 if (!_x_constrained) {
2731 _editor->snap_to_with_modifier (cx_frames, event);
2734 cx_frames = min (cx_frames, _point->line().maximum_time());
2736 float const fraction = 1.0 - (cy / _point->line().height());
2738 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2740 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2742 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2746 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2748 if (!movement_occurred) {
2752 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2753 _editor->reset_point_selection ();
2757 motion (event, false);
2760 _point->line().end_drag ();
2761 _editor->session()->commit_reversible_command ();
2765 ControlPointDrag::aborted (bool)
2767 _point->line().reset ();
2771 ControlPointDrag::active (Editing::MouseMode m)
2773 if (m == Editing::MouseGain) {
2774 /* always active in mouse gain */
2778 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2779 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2782 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2785 _cumulative_y_drag (0)
2787 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2791 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2793 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2796 _item = &_line->grab_item ();
2798 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2799 origin, and ditto for y.
2802 double cx = event->button.x;
2803 double cy = event->button.y;
2805 _line->parent_group().w2i (cx, cy);
2807 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2812 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2813 /* no adjacent points */
2817 Drag::start_grab (event, _editor->cursors()->fader);
2819 /* store grab start in parent frame */
2824 double fraction = 1.0 - (cy / _line->height());
2826 _line->start_drag_line (before, after, fraction);
2828 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2829 event->button.x + 10, event->button.y + 10);
2831 _editor->show_verbose_canvas_cursor ();
2835 LineDrag::motion (GdkEvent* event, bool)
2837 double dy = _drags->current_pointer_y() - last_pointer_y();
2839 if (event->button.state & Keyboard::SecondaryModifier) {
2843 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2845 _cumulative_y_drag = cy - _fixed_grab_y;
2848 cy = min ((double) _line->height(), cy);
2850 double const fraction = 1.0 - (cy / _line->height());
2854 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2860 /* we are ignoring x position for this drag, so we can just pass in anything */
2861 _line->drag_motion (0, fraction, true, push);
2863 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2867 LineDrag::finished (GdkEvent* event, bool)
2869 motion (event, false);
2871 _editor->session()->commit_reversible_command ();
2875 LineDrag::aborted (bool)
2880 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2883 _cumulative_x_drag (0)
2885 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2889 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2891 Drag::start_grab (event);
2893 _line = reinterpret_cast<SimpleLine*> (_item);
2896 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2898 double cx = event->button.x;
2899 double cy = event->button.y;
2901 _item->property_parent().get_value()->w2i(cx, cy);
2903 /* store grab start in parent frame */
2904 _region_view_grab_x = cx;
2906 _before = _line->property_x1();
2908 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2910 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2914 FeatureLineDrag::motion (GdkEvent*, bool)
2916 double dx = _drags->current_pointer_x() - last_pointer_x();
2918 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2920 _cumulative_x_drag += dx;
2922 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2931 _line->property_x1() = cx;
2932 _line->property_x2() = cx;
2934 _before = _line->property_x1();
2938 FeatureLineDrag::finished (GdkEvent*, bool)
2940 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2941 _arv->update_transient(_before, _line->property_x1());
2945 FeatureLineDrag::aborted (bool)
2950 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2953 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2957 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2959 Drag::start_grab (event);
2960 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2964 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2971 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2973 framepos_t grab = grab_frame ();
2974 if (Config->get_rubberbanding_snaps_to_grid ()) {
2975 _editor->snap_to_with_modifier (grab, event);
2978 /* base start and end on initial click position */
2988 if (_drags->current_pointer_y() < grab_y()) {
2989 y1 = _drags->current_pointer_y();
2992 y2 = _drags->current_pointer_y();
2997 if (start != end || y1 != y2) {
2999 double x1 = _editor->frame_to_pixel (start);
3000 double x2 = _editor->frame_to_pixel (end);
3002 _editor->rubberband_rect->property_x1() = x1;
3003 _editor->rubberband_rect->property_y1() = y1;
3004 _editor->rubberband_rect->property_x2() = x2;
3005 _editor->rubberband_rect->property_y2() = y2;
3007 _editor->rubberband_rect->show();
3008 _editor->rubberband_rect->raise_to_top();
3010 _editor->show_verbose_time_cursor (pf, 10);
3015 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3017 if (movement_occurred) {
3019 motion (event, false);
3022 if (_drags->current_pointer_y() < grab_y()) {
3023 y1 = _drags->current_pointer_y();
3026 y2 = _drags->current_pointer_y();
3031 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3034 _editor->begin_reversible_command (_("rubberband selection"));
3036 if (grab_frame() < last_pointer_frame()) {
3037 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3039 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3043 _editor->commit_reversible_command ();
3047 if (!getenv("ARDOUR_SAE")) {
3048 _editor->selection->clear_tracks();
3050 _editor->selection->clear_regions();
3051 _editor->selection->clear_points ();
3052 _editor->selection->clear_lines ();
3055 _editor->rubberband_rect->hide();
3059 RubberbandSelectDrag::aborted (bool)
3061 _editor->rubberband_rect->hide ();
3064 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3065 : RegionDrag (e, i, p, v)
3067 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3071 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3073 Drag::start_grab (event, cursor);
3075 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3079 TimeFXDrag::motion (GdkEvent* event, bool)
3081 RegionView* rv = _primary;
3083 framepos_t const pf = adjusted_current_frame (event);
3085 if (pf > rv->region()->position()) {
3086 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3089 _editor->show_verbose_time_cursor (pf, 10);
3093 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3095 _primary->get_time_axis_view().hide_timestretch ();
3097 if (!movement_occurred) {
3101 if (last_pointer_frame() < _primary->region()->position()) {
3102 /* backwards drag of the left edge - not usable */
3106 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3108 float percentage = (double) newlen / (double) _primary->region()->length();
3110 #ifndef USE_RUBBERBAND
3111 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3112 if (_primary->region()->data_type() == DataType::AUDIO) {
3113 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3117 _editor->begin_reversible_command (_("timestretch"));
3119 // XXX how do timeFX on multiple regions ?
3124 if (_editor->time_stretch (rs, percentage) == -1) {
3125 error << _("An error occurred while executing time stretch operation") << endmsg;
3130 TimeFXDrag::aborted (bool)
3132 _primary->get_time_axis_view().hide_timestretch ();
3135 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3138 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3142 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3144 Drag::start_grab (event);
3148 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3150 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3154 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3156 if (movement_occurred && _editor->session()) {
3157 /* make sure we stop */
3158 _editor->session()->request_transport_speed (0.0);
3163 ScrubDrag::aborted (bool)
3168 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3172 , _original_pointer_time_axis (-1)
3173 , _last_pointer_time_axis (-1)
3175 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3179 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3181 if (_editor->session() == 0) {
3185 Gdk::Cursor* cursor = 0;
3187 switch (_operation) {
3188 case CreateSelection:
3189 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3194 cursor = _editor->cursors()->selector;
3195 Drag::start_grab (event, cursor);
3198 case SelectionStartTrim:
3199 if (_editor->clicked_axisview) {
3200 _editor->clicked_axisview->order_selection_trims (_item, true);
3202 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3205 case SelectionEndTrim:
3206 if (_editor->clicked_axisview) {
3207 _editor->clicked_axisview->order_selection_trims (_item, false);
3209 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3213 Drag::start_grab (event, cursor);
3217 if (_operation == SelectionMove) {
3218 _editor->show_verbose_time_cursor (_editor->selection->time[_editor->clicked_selection].start, 10);
3220 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3223 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3227 SelectionDrag::setup_pointer_frame_offset ()
3229 switch (_operation) {
3230 case CreateSelection:
3231 _pointer_frame_offset = 0;
3234 case SelectionStartTrim:
3236 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3239 case SelectionEndTrim:
3240 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3246 SelectionDrag::motion (GdkEvent* event, bool first_move)
3248 framepos_t start = 0;
3252 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3253 if (pending_time_axis.first == 0) {
3257 framepos_t const pending_position = adjusted_current_frame (event);
3259 /* only alter selection if things have changed */
3261 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3265 switch (_operation) {
3266 case CreateSelection:
3268 framepos_t grab = grab_frame ();
3271 _editor->snap_to (grab);
3274 if (pending_position < grab_frame()) {
3275 start = pending_position;
3278 end = pending_position;
3282 /* first drag: Either add to the selection
3283 or create a new selection
3289 /* adding to the selection */
3290 _editor->set_selected_track_as_side_effect (Selection::Add);
3291 //_editor->selection->add (_editor->clicked_axisview);
3292 _editor->clicked_selection = _editor->selection->add (start, end);
3297 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3298 //_editor->selection->set (_editor->clicked_axisview);
3299 _editor->set_selected_track_as_side_effect (Selection::Set);
3302 _editor->clicked_selection = _editor->selection->set (start, end);
3306 /* select the track that we're in */
3307 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3308 // _editor->set_selected_track_as_side_effect (Selection::Add);
3309 _editor->selection->add (pending_time_axis.first);
3310 _added_time_axes.push_back (pending_time_axis.first);
3313 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3314 tracks that we selected in the first place.
3317 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3318 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3320 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3321 while (i != _added_time_axes.end()) {
3323 list<TimeAxisView*>::iterator tmp = i;
3326 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3327 _editor->selection->remove (*i);
3328 _added_time_axes.remove (*i);
3337 case SelectionStartTrim:
3339 start = _editor->selection->time[_editor->clicked_selection].start;
3340 end = _editor->selection->time[_editor->clicked_selection].end;
3342 if (pending_position > end) {
3345 start = pending_position;
3349 case SelectionEndTrim:
3351 start = _editor->selection->time[_editor->clicked_selection].start;
3352 end = _editor->selection->time[_editor->clicked_selection].end;
3354 if (pending_position < start) {
3357 end = pending_position;
3364 start = _editor->selection->time[_editor->clicked_selection].start;
3365 end = _editor->selection->time[_editor->clicked_selection].end;
3367 length = end - start;
3369 start = pending_position;
3370 _editor->snap_to (start);
3372 end = start + length;
3377 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3378 _editor->start_canvas_autoscroll (1, 0);
3382 _editor->selection->replace (_editor->clicked_selection, start, end);
3385 if (_operation == SelectionMove) {
3386 _editor->show_verbose_time_cursor(start, 10);
3388 _editor->show_verbose_time_cursor(pending_position, 10);
3393 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3395 Session* s = _editor->session();
3397 if (movement_occurred) {
3398 motion (event, false);
3399 /* XXX this is not object-oriented programming at all. ick */
3400 if (_editor->selection->time.consolidate()) {
3401 _editor->selection->TimeChanged ();
3404 /* XXX what if its a music time selection? */
3405 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3406 s->request_play_range (&_editor->selection->time, true);
3411 /* just a click, no pointer movement.*/
3413 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3414 _editor->selection->clear_time();
3417 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3418 _editor->selection->set (_editor->clicked_axisview);
3421 if (s && s->get_play_range () && s->transport_rolling()) {
3422 s->request_stop (false, false);
3427 _editor->stop_canvas_autoscroll ();
3431 SelectionDrag::aborted (bool)
3436 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3441 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3443 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3444 physical_screen_height (_editor->get_window()));
3445 _drag_rect->hide ();
3447 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3448 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3452 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3454 if (_editor->session() == 0) {
3458 Gdk::Cursor* cursor = 0;
3460 if (!_editor->temp_location) {
3461 _editor->temp_location = new Location (*_editor->session());
3464 switch (_operation) {
3465 case CreateRangeMarker:
3466 case CreateTransportMarker:
3467 case CreateCDMarker:
3469 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3474 cursor = _editor->cursors()->selector;
3478 Drag::start_grab (event, cursor);
3480 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3484 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3486 framepos_t start = 0;
3488 ArdourCanvas::SimpleRect *crect;
3490 switch (_operation) {
3491 case CreateRangeMarker:
3492 crect = _editor->range_bar_drag_rect;
3494 case CreateTransportMarker:
3495 crect = _editor->transport_bar_drag_rect;
3497 case CreateCDMarker:
3498 crect = _editor->cd_marker_bar_drag_rect;
3501 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3506 framepos_t const pf = adjusted_current_frame (event);
3508 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3509 framepos_t grab = grab_frame ();
3510 _editor->snap_to (grab);
3512 if (pf < grab_frame()) {
3520 /* first drag: Either add to the selection
3521 or create a new selection.
3526 _editor->temp_location->set (start, end);
3530 update_item (_editor->temp_location);
3532 //_drag_rect->raise_to_top();
3537 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3538 _editor->start_canvas_autoscroll (1, 0);
3542 _editor->temp_location->set (start, end);
3544 double x1 = _editor->frame_to_pixel (start);
3545 double x2 = _editor->frame_to_pixel (end);
3546 crect->property_x1() = x1;
3547 crect->property_x2() = x2;
3549 update_item (_editor->temp_location);
3552 _editor->show_verbose_time_cursor (pf, 10);
3557 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3559 Location * newloc = 0;
3563 if (movement_occurred) {
3564 motion (event, false);
3567 switch (_operation) {
3568 case CreateRangeMarker:
3569 case CreateCDMarker:
3571 _editor->begin_reversible_command (_("new range marker"));
3572 XMLNode &before = _editor->session()->locations()->get_state();
3573 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3574 if (_operation == CreateCDMarker) {
3575 flags = Location::IsRangeMarker | Location::IsCDMarker;
3576 _editor->cd_marker_bar_drag_rect->hide();
3579 flags = Location::IsRangeMarker;
3580 _editor->range_bar_drag_rect->hide();
3582 newloc = new Location (
3583 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3586 _editor->session()->locations()->add (newloc, true);
3587 XMLNode &after = _editor->session()->locations()->get_state();
3588 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3589 _editor->commit_reversible_command ();
3593 case CreateTransportMarker:
3594 // popup menu to pick loop or punch
3595 _editor->new_transport_marker_context_menu (&event->button, _item);
3599 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3601 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3606 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3608 if (end == max_framepos) {
3609 end = _editor->session()->current_end_frame ();
3612 if (start == max_framepos) {
3613 start = _editor->session()->current_start_frame ();
3616 switch (_editor->mouse_mode) {
3618 /* find the two markers on either side and then make the selection from it */
3619 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3623 /* find the two markers on either side of the click and make the range out of it */
3624 _editor->selection->set (start, end);
3633 _editor->stop_canvas_autoscroll ();
3637 RangeMarkerBarDrag::aborted (bool)
3643 RangeMarkerBarDrag::update_item (Location* location)
3645 double const x1 = _editor->frame_to_pixel (location->start());
3646 double const x2 = _editor->frame_to_pixel (location->end());
3648 _drag_rect->property_x1() = x1;
3649 _drag_rect->property_x2() = x2;
3652 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3656 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3660 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3662 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3663 Drag::start_grab (event, _editor->cursors()->zoom_out);
3666 Drag::start_grab (event, _editor->cursors()->zoom_in);
3670 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3674 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3679 framepos_t const pf = adjusted_current_frame (event);
3681 framepos_t grab = grab_frame ();
3682 _editor->snap_to_with_modifier (grab, event);
3684 /* base start and end on initial click position */
3696 _editor->zoom_rect->show();
3697 _editor->zoom_rect->raise_to_top();
3700 _editor->reposition_zoom_rect(start, end);
3702 _editor->show_verbose_time_cursor (pf, 10);
3707 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3709 if (movement_occurred) {
3710 motion (event, false);
3712 if (grab_frame() < last_pointer_frame()) {
3713 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3715 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3718 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3721 _editor->zoom_rect->hide();
3725 MouseZoomDrag::aborted (bool)
3727 _editor->zoom_rect->hide ();
3730 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3732 , _cumulative_dx (0)
3733 , _cumulative_dy (0)
3735 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3737 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3738 _region = &_primary->region_view ();
3739 _note_height = _region->midi_stream_view()->note_height ();
3743 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3745 Drag::start_grab (event);
3747 if (!(_was_selected = _primary->selected())) {
3749 /* tertiary-click means extend selection - we'll do that on button release,
3750 so don't add it here, because otherwise we make it hard to figure
3751 out the "extend-to" range.
3754 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3757 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3760 _region->note_selected (_primary, true);
3762 _region->unique_select (_primary);
3768 /** @return Current total drag x change in frames */
3770 NoteDrag::total_dx () const
3773 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3775 /* primary note time */
3776 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3778 /* new time of the primary note relative to the region position */
3779 frameoffset_t const st = n + dx;
3781 /* snap and return corresponding delta */
3782 return _region->snap_frame_to_frame (st) - n;
3785 /** @return Current total drag y change in notes */
3787 NoteDrag::total_dy () const
3789 /* this is `backwards' to make increasing note number go in the right direction */
3790 double const dy = _drags->current_pointer_y() - grab_y();
3795 if (abs (dy) >= _note_height) {
3797 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3799 ndy = (int8_t) floor (dy / _note_height / 2.0);
3803 /* more positive value = higher pitch and higher y-axis position on track,
3804 which is the inverse of the X-centric geometric universe
3811 NoteDrag::motion (GdkEvent *, bool)
3813 /* Total change in x and y since the start of the drag */
3814 frameoffset_t const dx = total_dx ();
3815 int8_t const dy = -total_dy ();
3817 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3818 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3819 double const tdy = dy * _note_height - _cumulative_dy;
3822 _cumulative_dx += tdx;
3823 _cumulative_dy += tdy;
3825 int8_t note_delta = total_dy();
3827 _region->move_selection (tdx, tdy, note_delta);
3830 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3831 (int) floor (_primary->note()->note() + note_delta));
3833 _editor->show_verbose_canvas_cursor_with (buf);
3838 NoteDrag::finished (GdkEvent* ev, bool moved)
3841 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3843 if (_was_selected) {
3844 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3846 _region->note_deselected (_primary);
3849 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3850 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3852 if (!extend && !add && _region->selection_size() > 1) {
3853 _region->unique_select (_primary);
3854 } else if (extend) {
3855 _region->note_selected (_primary, true, true);
3857 /* it was added during button press */
3862 _region->note_dropped (_primary, total_dx(), total_dy());
3867 NoteDrag::aborted (bool)
3872 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3873 : Drag (editor, item)
3875 , _nothing_to_drag (false)
3877 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3879 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3882 /* get all lines in the automation view */
3883 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3885 /* find those that overlap the ranges being dragged */
3886 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3887 while (i != lines.end ()) {
3888 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3891 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3893 /* check this range against all the AudioRanges that we are using */
3894 list<AudioRange>::const_iterator k = _ranges.begin ();
3895 while (k != _ranges.end()) {
3896 if (k->coverage (r.first, r.second) != OverlapNone) {
3902 /* add it to our list if it overlaps at all */
3903 if (k != _ranges.end()) {
3908 _lines.push_back (n);
3914 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3918 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3920 Drag::start_grab (event, cursor);
3922 /* Get line states before we start changing things */
3923 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3924 i->state = &i->line->get_state ();
3927 if (_ranges.empty()) {
3929 /* No selected time ranges: drag all points */
3930 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3931 uint32_t const N = i->line->npoints ();
3932 for (uint32_t j = 0; j < N; ++j) {
3933 i->points.push_back (i->line->nth (j));
3939 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3941 framecnt_t const half = (i->start + i->end) / 2;
3943 /* find the line that this audio range starts in */
3944 list<Line>::iterator j = _lines.begin();
3945 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3949 if (j != _lines.end()) {
3950 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3952 /* j is the line that this audio range starts in; fade into it;
3953 64 samples length plucked out of thin air.
3956 framepos_t a = i->start + 64;
3961 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3962 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3964 the_list->add (p, the_list->eval (p));
3965 j->line->add_always_in_view (p);
3966 the_list->add (q, the_list->eval (q));
3967 j->line->add_always_in_view (q);
3970 /* same thing for the end */
3973 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3977 if (j != _lines.end()) {
3978 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3980 /* j is the line that this audio range starts in; fade out of it;
3981 64 samples length plucked out of thin air.
3984 framepos_t b = i->end - 64;
3989 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3990 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
3992 the_list->add (p, the_list->eval (p));
3993 j->line->add_always_in_view (p);
3994 the_list->add (q, the_list->eval (q));
3995 j->line->add_always_in_view (q);
3999 _nothing_to_drag = true;
4001 /* Find all the points that should be dragged and put them in the relevant
4002 points lists in the Line structs.
4005 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4007 uint32_t const N = i->line->npoints ();
4008 for (uint32_t j = 0; j < N; ++j) {
4010 /* here's a control point on this line */
4011 ControlPoint* p = i->line->nth (j);
4012 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4014 /* see if it's inside a range */
4015 list<AudioRange>::const_iterator k = _ranges.begin ();
4016 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4020 if (k != _ranges.end()) {
4021 /* dragging this point */
4022 _nothing_to_drag = false;
4023 i->points.push_back (p);
4029 if (_nothing_to_drag) {
4033 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4034 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4039 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4041 if (_nothing_to_drag) {
4045 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4046 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4048 /* we are ignoring x position for this drag, so we can just pass in anything */
4049 i->line->drag_motion (0, f, true, false);
4054 AutomationRangeDrag::finished (GdkEvent* event, bool)
4056 if (_nothing_to_drag) {
4060 motion (event, false);
4061 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4062 i->line->end_drag ();
4063 i->line->clear_always_in_view ();
4066 _editor->session()->commit_reversible_command ();
4070 AutomationRangeDrag::aborted (bool)
4072 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4073 i->line->clear_always_in_view ();
4078 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4081 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4082 layer = v->region()->layer ();
4083 initial_y = v->get_canvas_group()->property_y ();
4084 initial_playlist = v->region()->playlist ();
4085 initial_position = v->region()->position ();
4086 initial_end = v->region()->position () + v->region()->length ();
4089 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4093 , _cumulative_dx (0)
4095 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4099 PatchChangeDrag::motion (GdkEvent* ev, bool)
4101 framepos_t f = adjusted_current_frame (ev);
4102 boost::shared_ptr<Region> r = _region_view->region ();
4103 f = max (f, r->position ());
4104 f = min (f, r->last_frame ());
4106 framecnt_t const dxf = f - grab_frame();
4107 double const dxu = _editor->frame_to_unit (dxf);
4108 _patch_change->move (dxu - _cumulative_dx, 0);
4109 _cumulative_dx = dxu;
4113 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4115 if (!movement_occurred) {
4119 boost::shared_ptr<Region> r (_region_view->region ());
4121 framepos_t f = adjusted_current_frame (ev);
4122 f = max (f, r->position ());
4123 f = min (f, r->last_frame ());
4125 _region_view->move_patch_change (
4127 _region_view->frames_to_beats (f - r->position() - r->start())
4132 PatchChangeDrag::aborted (bool)
4134 _patch_change->move (-_cumulative_dx, 0);
4138 PatchChangeDrag::setup_pointer_frame_offset ()
4140 boost::shared_ptr<Region> region = _region_view->region ();
4141 _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_patch_change->patch()->time()) - region->position() + region->start();