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.
22 #include "pbd/memento_command.h"
23 #include "pbd/basename.h"
24 #include "pbd/stateful_diff_command.h"
26 #include "gtkmm2ext/utils.h"
28 #include "ardour/session.h"
29 #include "ardour/dB.h"
30 #include "ardour/region_factory.h"
34 #include "audio_region_view.h"
35 #include "midi_region_view.h"
36 #include "ardour_ui.h"
37 #include "gui_thread.h"
38 #include "control_point.h"
40 #include "region_gain_line.h"
41 #include "editor_drag.h"
42 #include "audio_time_axis.h"
43 #include "midi_time_axis.h"
44 #include "canvas-note.h"
45 #include "selection.h"
46 #include "midi_selection.h"
47 #include "automation_time_axis.h"
51 using namespace ARDOUR;
54 using namespace Gtkmm2ext;
55 using namespace Editing;
56 using namespace ArdourCanvas;
58 using Gtkmm2ext::Keyboard;
60 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
62 DragManager::DragManager (Editor* e)
65 , _current_pointer_frame (0)
70 DragManager::~DragManager ()
75 /** Call abort for each active drag */
81 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
92 DragManager::add (Drag* d)
94 d->set_manager (this);
99 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
101 d->set_manager (this);
102 _drags.push_back (d);
107 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
109 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
111 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
112 (*i)->start_grab (e, c);
116 /** Call end_grab for each active drag.
117 * @return true if any drag reported movement having occurred.
120 DragManager::end_grab (GdkEvent* e)
125 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
126 bool const t = (*i)->end_grab (e);
141 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
145 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
147 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
148 bool const t = (*i)->motion_handler (e, from_autoscroll);
159 DragManager::have_item (ArdourCanvas::Item* i) const
161 list<Drag*>::const_iterator j = _drags.begin ();
162 while (j != _drags.end() && (*j)->item () != i) {
166 return j != _drags.end ();
169 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
172 , _pointer_frame_offset (0)
173 , _move_threshold_passed (false)
174 , _raw_grab_frame (0)
176 , _last_pointer_frame (0)
182 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
188 cursor = _editor->which_grabber_cursor ();
191 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
195 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
198 cursor = _editor->which_grabber_cursor ();
201 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
203 if (Keyboard::is_button2_event (&event->button)) {
204 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
205 _y_constrained = true;
206 _x_constrained = false;
208 _y_constrained = false;
209 _x_constrained = true;
212 _x_constrained = false;
213 _y_constrained = false;
216 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
217 _grab_frame = adjusted_frame (_raw_grab_frame, event);
218 _last_pointer_frame = _grab_frame;
219 _last_pointer_x = _grab_x;
220 _last_pointer_y = _grab_y;
222 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
226 if (_editor->session() && _editor->session()->transport_rolling()) {
229 _was_rolling = false;
232 switch (_editor->snap_type()) {
233 case SnapToRegionStart:
234 case SnapToRegionEnd:
235 case SnapToRegionSync:
236 case SnapToRegionBoundary:
237 _editor->build_region_boundary_cache ();
244 /** Call to end a drag `successfully'. Ungrabs item and calls
245 * subclass' finished() method.
247 * @param event GDK event, or 0.
248 * @return true if some movement occurred, otherwise false.
251 Drag::end_grab (GdkEvent* event)
253 _editor->stop_canvas_autoscroll ();
255 _item->ungrab (event ? event->button.time : 0);
257 finished (event, _move_threshold_passed);
259 _editor->hide_verbose_canvas_cursor();
261 return _move_threshold_passed;
265 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
269 if (f > _pointer_frame_offset) {
270 pos = f - _pointer_frame_offset;
274 _editor->snap_to_with_modifier (pos, event);
281 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
283 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
287 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
289 /* check to see if we have moved in any way that matters since the last motion event */
290 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
291 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
295 pair<framecnt_t, int> const threshold = move_threshold ();
297 bool const old_move_threshold_passed = _move_threshold_passed;
299 if (!from_autoscroll && !_move_threshold_passed) {
301 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
302 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
304 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
307 if (active (_editor->mouse_mode) && _move_threshold_passed) {
309 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
310 if (!from_autoscroll) {
311 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
314 motion (event, _move_threshold_passed != old_move_threshold_passed);
316 _last_pointer_x = _drags->current_pointer_x ();
317 _last_pointer_y = _drags->current_pointer_y ();
318 _last_pointer_frame = adjusted_current_frame (event);
326 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
336 _editor->stop_canvas_autoscroll ();
337 _editor->hide_verbose_canvas_cursor ();
340 struct EditorOrderTimeAxisViewSorter {
341 bool operator() (TimeAxisView* a, TimeAxisView* b) {
342 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
343 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
345 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
349 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
353 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
355 /* Make a list of non-hidden tracks to refer to during the drag */
357 TrackViewList track_views = _editor->track_views;
358 track_views.sort (EditorOrderTimeAxisViewSorter ());
360 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
361 if (!(*i)->hidden()) {
363 _time_axis_views.push_back (*i);
365 TimeAxisView::Children children_list = (*i)->get_child_list ();
366 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
367 _time_axis_views.push_back (j->get());
372 /* the list of views can be empty at this point if this is a region list-insert drag
375 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
376 _views.push_back (DraggingView (*i, this));
379 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
383 RegionDrag::region_going_away (RegionView* v)
385 list<DraggingView>::iterator i = _views.begin ();
386 while (i != _views.end() && i->view != v) {
390 if (i != _views.end()) {
395 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
397 RegionDrag::find_time_axis_view (TimeAxisView* t) const
400 int const N = _time_axis_views.size ();
401 while (i < N && _time_axis_views[i] != t) {
412 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
413 : RegionDrag (e, i, p, v),
422 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
424 Drag::start_grab (event, cursor);
426 _editor->show_verbose_time_cursor (_last_frame_position, 10);
428 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
429 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
430 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
434 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
436 /* compute the amount of pointer motion in frames, and where
437 the region would be if we moved it by that much.
439 *pending_region_position = adjusted_current_frame (event);
441 framepos_t sync_frame;
442 framecnt_t sync_offset;
445 sync_offset = _primary->region()->sync_offset (sync_dir);
447 /* we don't handle a sync point that lies before zero.
449 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
451 sync_frame = *pending_region_position + (sync_dir*sync_offset);
453 _editor->snap_to_with_modifier (sync_frame, event);
455 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
458 *pending_region_position = _last_frame_position;
461 if (*pending_region_position > max_framepos - _primary->region()->length()) {
462 *pending_region_position = _last_frame_position;
467 /* in locked edit mode, reverse the usual meaning of _x_constrained */
468 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
470 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
472 /* x movement since last time */
473 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
475 /* total x movement */
476 framecnt_t total_dx = *pending_region_position;
477 if (regions_came_from_canvas()) {
478 total_dx = total_dx - grab_frame () + _pointer_frame_offset;
481 /* check that no regions have gone off the start of the session */
482 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
483 if ((i->view->region()->position() + total_dx) < 0) {
485 *pending_region_position = _last_frame_position;
490 _last_frame_position = *pending_region_position;
497 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
499 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
500 int const n = i->time_axis_view + delta_track;
501 if (n < 0 || n >= int (_time_axis_views.size())) {
502 /* off the top or bottom track */
506 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
507 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
508 /* not a track, or the wrong type */
512 int const l = i->layer + delta_layer;
513 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
514 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
515 If it has, the layers will be munged later anyway, so it's ok.
521 /* all regions being dragged are ok with this change */
526 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
528 assert (!_views.empty ());
530 /* Find the TimeAxisView that the pointer is now over */
531 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
533 /* Bail early if we're not over a track */
534 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
535 if (!rtv || !rtv->is_track()) {
536 _editor->hide_verbose_canvas_cursor ();
540 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
542 /* Here's the current pointer position in terms of time axis view and layer */
543 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
544 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
546 /* Work out the change in x */
547 framepos_t pending_region_position;
548 double const x_delta = compute_x_delta (event, &pending_region_position);
550 /* Work out the change in y */
551 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
552 int delta_layer = current_pointer_layer - _last_pointer_layer;
554 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
555 /* this y movement is not allowed, so do no y movement this time */
556 delta_time_axis_view = 0;
560 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
561 /* haven't reached next snap point, and we're not switching
562 trackviews nor layers. nothing to do.
567 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
569 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
571 RegionView* rv = i->view;
573 if (rv->region()->locked()) {
579 /* here we are calculating the y distance from the
580 top of the first track view to the top of the region
581 area of the track view that we're working on */
583 /* this x value is just a dummy value so that we have something
588 /* distance from the top of this track view to the region area
589 of our track view is always 1 */
593 /* convert to world coordinates, ie distance from the top of
596 rv->get_canvas_frame()->i2w (ix1, iy1);
598 /* compensate for the ruler section and the vertical scrollbar position */
599 iy1 += _editor->get_trackview_group_vertical_offset ();
601 // hide any dependent views
603 rv->get_time_axis_view().hide_dependent_views (*rv);
606 reparent to a non scrolling group so that we can keep the
607 region selection above all time axis views.
608 reparenting means we have to move the rv as the two
609 parent groups have different coordinates.
612 rv->get_canvas_group()->property_y() = iy1 - 1;
613 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
615 rv->fake_set_opaque (true);
618 /* Work out the change in y position of this region view */
622 /* If we have moved tracks, we'll fudge the layer delta so that the
623 region gets moved back onto layer 0 on its new track; this avoids
624 confusion when dragging regions from non-zero layers onto different
627 int this_delta_layer = delta_layer;
628 if (delta_time_axis_view != 0) {
629 this_delta_layer = - i->layer;
632 /* Move this region to layer 0 on its old track */
633 StreamView* lv = _time_axis_views[i->time_axis_view]->view ();
634 if (lv->layer_display() == Stacked) {
635 y_delta -= (lv->layers() - i->layer - 1) * lv->child_height ();
638 /* Now move it to its right layer on the current track */
639 StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view ();
640 if (cv->layer_display() == Stacked) {
641 y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height ();
645 if (delta_time_axis_view > 0) {
646 for (int j = 0; j < delta_time_axis_view; ++j) {
647 y_delta += _time_axis_views[i->time_axis_view + j]->current_height ();
650 /* start by subtracting the height of the track above where we are now */
651 for (int j = 1; j <= -delta_time_axis_view; ++j) {
652 y_delta -= _time_axis_views[i->time_axis_view - j]->current_height ();
657 rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ());
659 /* Update the DraggingView */
660 i->time_axis_view += delta_time_axis_view;
661 i->layer += this_delta_layer;
664 _editor->mouse_brush_insert_region (rv, pending_region_position);
666 rv->move (x_delta, y_delta);
669 } /* foreach region */
671 _total_x_delta += x_delta;
674 _editor->cursor_group->raise_to_top();
677 if (x_delta != 0 && !_brushing) {
678 _editor->show_verbose_time_cursor (_last_frame_position, 10);
681 _last_pointer_time_axis_view += delta_time_axis_view;
682 _last_pointer_layer += delta_layer;
686 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
688 if (_copy && first_move) {
690 /* duplicate the regionview(s) and region(s) */
692 list<DraggingView> new_regionviews;
694 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
696 RegionView* rv = i->view;
697 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
698 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
700 const boost::shared_ptr<const Region> original = rv->region();
701 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
702 region_copy->set_position (original->position(), this);
706 boost::shared_ptr<AudioRegion> audioregion_copy
707 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
709 nrv = new AudioRegionView (*arv, audioregion_copy);
711 boost::shared_ptr<MidiRegion> midiregion_copy
712 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
713 nrv = new MidiRegionView (*mrv, midiregion_copy);
718 nrv->get_canvas_group()->show ();
719 new_regionviews.push_back (DraggingView (nrv, this));
721 /* swap _primary to the copy */
723 if (rv == _primary) {
727 /* ..and deselect the one we copied */
729 rv->set_selected (false);
732 if (!new_regionviews.empty()) {
734 /* reflect the fact that we are dragging the copies */
736 _views = new_regionviews;
738 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
741 sync the canvas to what we think is its current state
742 without it, the canvas seems to
743 "forget" to update properly after the upcoming reparent()
744 ..only if the mouse is in rapid motion at the time of the grab.
745 something to do with regionview creation taking so long?
747 _editor->update_canvas_now();
751 RegionMotionDrag::motion (event, first_move);
755 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
757 if (!movement_occurred) {
762 /* reverse this here so that we have the correct logic to finalize
766 if (Config->get_edit_mode() == Lock) {
767 _x_constrained = !_x_constrained;
770 assert (!_views.empty ());
772 bool const changed_position = (_last_frame_position != _primary->region()->position());
773 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
774 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
776 _editor->update_canvas_now ();
798 RegionMoveDrag::finished_copy (
799 bool const changed_position,
800 bool const changed_tracks,
801 framecnt_t const drag_delta
804 RegionSelection new_views;
805 PlaylistSet modified_playlists;
806 list<RegionView*> views_to_delete;
809 /* all changes were made during motion event handlers */
811 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
815 _editor->commit_reversible_command ();
819 if (_x_constrained) {
820 _editor->begin_reversible_command (_("fixed time region copy"));
822 _editor->begin_reversible_command (_("region copy"));
825 /* insert the regions into their new playlists */
826 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
828 if (i->view->region()->locked()) {
834 if (changed_position && !_x_constrained) {
835 where = i->view->region()->position() - drag_delta;
837 where = i->view->region()->position();
840 RegionView* new_view = insert_region_into_playlist (
841 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
848 new_views.push_back (new_view);
850 /* we don't need the copied RegionView any more */
851 views_to_delete.push_back (i->view);
854 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
855 because when views are deleted they are automagically removed from _views, which messes
858 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
862 /* If we've created new regions either by copying or moving
863 to a new track, we want to replace the old selection with the new ones
866 if (new_views.size() > 0) {
867 _editor->selection->set (new_views);
870 /* write commands for the accumulated diffs for all our modified playlists */
871 add_stateful_diff_commands_for_playlists (modified_playlists);
873 _editor->commit_reversible_command ();
877 RegionMoveDrag::finished_no_copy (
878 bool const changed_position,
879 bool const changed_tracks,
880 framecnt_t const drag_delta
883 RegionSelection new_views;
884 PlaylistSet modified_playlists;
885 PlaylistSet frozen_playlists;
888 /* all changes were made during motion event handlers */
889 _editor->commit_reversible_command ();
893 if (_x_constrained) {
894 _editor->begin_reversible_command (_("fixed time region drag"));
896 _editor->begin_reversible_command (_("region drag"));
899 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
901 RegionView* rv = i->view;
903 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
904 layer_t const dest_layer = i->layer;
906 if (rv->region()->locked()) {
913 if (changed_position && !_x_constrained) {
914 where = rv->region()->position() - drag_delta;
916 where = rv->region()->position();
919 if (changed_tracks) {
921 /* insert into new playlist */
923 RegionView* new_view = insert_region_into_playlist (
924 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
932 new_views.push_back (new_view);
934 /* remove from old playlist */
936 /* the region that used to be in the old playlist is not
937 moved to the new one - we use a copy of it. as a result,
938 any existing editor for the region should no longer be
941 rv->hide_region_editor();
942 rv->fake_set_opaque (false);
944 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
948 rv->region()->clear_changes ();
951 motion on the same track. plonk the previously reparented region
952 back to its original canvas group (its streamview).
953 No need to do anything for copies as they are fake regions which will be deleted.
956 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
957 rv->get_canvas_group()->property_y() = i->initial_y;
958 rv->get_time_axis_view().reveal_dependent_views (*rv);
960 /* just change the model */
962 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
964 if (dest_rtv->view()->layer_display() == Stacked) {
965 rv->region()->set_layer (dest_layer);
966 rv->region()->set_pending_explicit_relayer (true);
969 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
971 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
977 /* this movement may result in a crossfade being modified, so we need to get undo
978 data from the playlist as well as the region.
981 r = modified_playlists.insert (playlist);
983 playlist->clear_changes ();
986 rv->region()->set_position (where, (void*) this);
988 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
991 if (changed_tracks) {
993 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
994 was selected in all of them, then removing it from a playlist will have removed all
995 trace of it from _views (i.e. there were N regions selected, we removed 1,
996 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
997 corresponding regionview, and _views is now empty).
999 This could have invalidated any and all iterators into _views.
1001 The heuristic we use here is: if the region selection is empty, break out of the loop
1002 here. if the region selection is not empty, then restart the loop because we know that
1003 we must have removed at least the region(view) we've just been working on as well as any
1004 that we processed on previous iterations.
1006 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1007 we can just iterate.
1011 if (_views.empty()) {
1022 /* If we've created new regions either by copying or moving
1023 to a new track, we want to replace the old selection with the new ones
1026 if (new_views.size() > 0) {
1027 _editor->selection->set (new_views);
1030 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1034 /* write commands for the accumulated diffs for all our modified playlists */
1035 add_stateful_diff_commands_for_playlists (modified_playlists);
1037 _editor->commit_reversible_command ();
1040 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1041 * @param region Region to remove.
1042 * @param playlist playlist To remove from.
1043 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1044 * that clear_changes () is only called once per playlist.
1047 RegionMoveDrag::remove_region_from_playlist (
1048 boost::shared_ptr<Region> region,
1049 boost::shared_ptr<Playlist> playlist,
1050 PlaylistSet& modified_playlists
1053 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1056 playlist->clear_changes ();
1059 playlist->remove_region (region);
1063 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1064 * clearing the playlist's diff history first if necessary.
1065 * @param region Region to insert.
1066 * @param dest_rtv Destination RouteTimeAxisView.
1067 * @param dest_layer Destination layer.
1068 * @param where Destination position.
1069 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1070 * that clear_changes () is only called once per playlist.
1071 * @return New RegionView, or 0 if no insert was performed.
1074 RegionMoveDrag::insert_region_into_playlist (
1075 boost::shared_ptr<Region> region,
1076 RouteTimeAxisView* dest_rtv,
1079 PlaylistSet& modified_playlists
1082 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1083 if (!dest_playlist) {
1087 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1088 _new_region_view = 0;
1089 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1091 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1092 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1094 dest_playlist->clear_changes ();
1097 dest_playlist->add_region (region, where);
1099 if (dest_rtv->view()->layer_display() == Stacked) {
1100 region->set_layer (dest_layer);
1101 region->set_pending_explicit_relayer (true);
1106 assert (_new_region_view);
1108 return _new_region_view;
1112 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1114 _new_region_view = rv;
1118 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1120 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1121 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1123 _editor->session()->add_command (new StatefulDiffCommand (*i));
1132 RegionMoveDrag::aborted ()
1136 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1143 RegionMotionDrag::aborted ();
1148 RegionMotionDrag::aborted ()
1150 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1151 RegionView* rv = i->view;
1152 TimeAxisView* tv = &(rv->get_time_axis_view ());
1153 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1155 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1156 rv->get_canvas_group()->property_y() = 0;
1157 rv->get_time_axis_view().reveal_dependent_views (*rv);
1158 rv->fake_set_opaque (false);
1159 rv->move (-_total_x_delta, 0);
1160 rv->set_height (rtv->view()->child_height ());
1163 _editor->update_canvas_now ();
1166 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1167 : RegionMotionDrag (e, i, p, v, b),
1170 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1173 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1174 if (rtv && rtv->is_track()) {
1175 speed = rtv->track()->speed ();
1178 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1182 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1184 RegionMotionDrag::start_grab (event, c);
1186 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1189 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1190 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1192 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1194 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1195 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1197 _primary = v->view()->create_region_view (r, false, false);
1199 _primary->get_canvas_group()->show ();
1200 _primary->set_position (pos, 0);
1201 _views.push_back (DraggingView (_primary, this));
1203 _last_frame_position = pos;
1205 _item = _primary->get_canvas_group ();
1209 RegionInsertDrag::finished (GdkEvent *, bool)
1211 _editor->update_canvas_now ();
1213 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1215 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1216 _primary->get_canvas_group()->property_y() = 0;
1218 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1220 _editor->begin_reversible_command (_("insert region"));
1221 playlist->clear_changes ();
1222 playlist->add_region (_primary->region (), _last_frame_position);
1223 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1224 _editor->commit_reversible_command ();
1232 RegionInsertDrag::aborted ()
1239 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1240 : RegionMoveDrag (e, i, p, v, false, false)
1242 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1245 struct RegionSelectionByPosition {
1246 bool operator() (RegionView*a, RegionView* b) {
1247 return a->region()->position () < b->region()->position();
1252 RegionSpliceDrag::motion (GdkEvent* event, bool)
1254 /* Which trackview is this ? */
1256 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1257 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1258 layer_t layer = tvp.second;
1260 if (tv && tv->layer_display() == Overlaid) {
1264 /* The region motion is only processed if the pointer is over
1268 if (!tv || !tv->is_track()) {
1269 /* To make sure we hide the verbose canvas cursor when the mouse is
1270 not held over and audiotrack.
1272 _editor->hide_verbose_canvas_cursor ();
1278 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1284 RegionSelection copy (_editor->selection->regions);
1286 RegionSelectionByPosition cmp;
1289 framepos_t const pf = adjusted_current_frame (event);
1291 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1293 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1299 boost::shared_ptr<Playlist> playlist;
1301 if ((playlist = atv->playlist()) == 0) {
1305 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1310 if (pf < (*i)->region()->last_frame() + 1) {
1314 if (pf > (*i)->region()->first_frame()) {
1320 playlist->shuffle ((*i)->region(), dir);
1325 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1327 RegionMoveDrag::finished (event, movement_occurred);
1331 RegionSpliceDrag::aborted ()
1336 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1338 _view (dynamic_cast<MidiTimeAxisView*> (v))
1340 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1346 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1352 framepos_t const f = adjusted_current_frame (event);
1353 if (f < grab_frame()) {
1354 _region->set_position (f, this);
1357 /* again, don't use a zero-length region (see above) */
1358 framecnt_t const len = abs (f - grab_frame ());
1359 _region->set_length (len < 1 ? 1 : len, this);
1365 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1367 if (!movement_occurred) {
1372 _editor->commit_reversible_command ();
1377 RegionCreateDrag::add_region ()
1379 if (_editor->session()) {
1380 const TempoMap& map (_editor->session()->tempo_map());
1381 framecnt_t pos = grab_frame();
1382 const Meter& m = map.meter_at (pos);
1383 /* not that the frame rate used here can be affected by pull up/down which
1386 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1387 _region = _view->add_region (grab_frame(), len, false);
1392 RegionCreateDrag::aborted ()
1397 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1401 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1405 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1407 Gdk::Cursor* cursor;
1408 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1409 float x_fraction = cnote->mouse_x_fraction ();
1411 if (x_fraction > 0.0 && x_fraction < 0.25) {
1412 cursor = _editor->left_side_trim_cursor;
1414 cursor = _editor->right_side_trim_cursor;
1417 Drag::start_grab (event, cursor);
1419 region = &cnote->region_view();
1421 double const region_start = region->get_position_pixels();
1422 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1424 if (grab_x() <= middle_point) {
1425 cursor = _editor->left_side_trim_cursor;
1428 cursor = _editor->right_side_trim_cursor;
1432 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1434 if (event->motion.state & Keyboard::PrimaryModifier) {
1440 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1442 if (ms.size() > 1) {
1443 /* has to be relative, may make no sense otherwise */
1447 /* select this note; if it is already selected, preserve the existing selection,
1448 otherwise make this note the only one selected.
1450 region->note_selected (cnote, cnote->selected ());
1452 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1453 MidiRegionSelection::iterator next;
1456 (*r)->begin_resizing (at_front);
1462 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1464 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1465 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1466 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1471 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1473 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1474 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1475 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1480 NoteResizeDrag::aborted ()
1485 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1488 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1492 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1498 RegionGainDrag::finished (GdkEvent *, bool)
1504 RegionGainDrag::aborted ()
1509 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1510 : RegionDrag (e, i, p, v)
1511 , _have_transaction (false)
1513 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1517 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1520 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1521 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1523 if (tv && tv->is_track()) {
1524 speed = tv->track()->speed();
1527 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1528 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1529 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1531 framepos_t const pf = adjusted_current_frame (event);
1533 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1534 _operation = ContentsTrim;
1535 Drag::start_grab (event, _editor->trimmer_cursor);
1537 /* These will get overridden for a point trim.*/
1538 if (pf < (region_start + region_length/2)) {
1539 /* closer to start */
1540 _operation = StartTrim;
1541 Drag::start_grab (event, _editor->left_side_trim_cursor);
1544 _operation = EndTrim;
1545 Drag::start_grab (event, _editor->right_side_trim_cursor);
1549 switch (_operation) {
1551 _editor->show_verbose_time_cursor (region_start, 10);
1554 _editor->show_verbose_time_cursor (region_end, 10);
1557 _editor->show_verbose_time_cursor (pf, 10);
1561 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1562 i->view->region()->suspend_property_changes ();
1567 TrimDrag::motion (GdkEvent* event, bool first_move)
1569 RegionView* rv = _primary;
1572 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1573 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1574 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1576 if (tv && tv->is_track()) {
1577 speed = tv->track()->speed();
1580 framecnt_t const dt = adjusted_current_frame (event) - grab_frame ();
1586 switch (_operation) {
1588 trim_type = "Region start trim";
1591 trim_type = "Region end trim";
1594 trim_type = "Region content trim";
1598 _editor->begin_reversible_command (trim_type);
1599 _have_transaction = true;
1601 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1602 RegionView* rv = i->view;
1603 rv->fake_set_opaque (false);
1604 rv->enable_display (false);
1605 rv->region()->clear_changes ();
1607 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1610 arv->temporarily_hide_envelope ();
1613 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1614 insert_result = _editor->motion_frozen_playlists.insert (pl);
1616 if (insert_result.second) {
1622 bool non_overlap_trim = false;
1624 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1625 non_overlap_trim = true;
1628 switch (_operation) {
1630 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1631 i->view->trim_start (i->initial_position + dt, non_overlap_trim);
1636 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1637 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1643 bool swap_direction = false;
1645 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1646 swap_direction = true;
1649 framecnt_t frame_delta = 0;
1651 bool left_direction = false;
1652 if (last_pointer_frame() > adjusted_current_frame(event)) {
1653 left_direction = true;
1656 if (left_direction) {
1657 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1659 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1662 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1663 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1669 switch (_operation) {
1671 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->position() / speed), 10);
1674 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->last_frame() / speed), 10);
1677 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1684 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1686 if (movement_occurred) {
1687 motion (event, false);
1689 if (!_editor->selection->selected (_primary)) {
1690 _primary->thaw_after_trim ();
1693 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1694 i->view->thaw_after_trim ();
1695 i->view->enable_display (true);
1696 i->view->fake_set_opaque (true);
1697 if (_have_transaction) {
1698 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1702 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1706 _editor->motion_frozen_playlists.clear ();
1708 if (_have_transaction) {
1709 _editor->commit_reversible_command();
1713 /* no mouse movement */
1714 _editor->point_trim (event, adjusted_current_frame (event));
1717 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1718 i->view->region()->resume_property_changes ();
1723 TrimDrag::aborted ()
1725 /* Our motion method is changing model state, so use the Undo system
1726 to cancel. Perhaps not ideal, as this will leave an Undo point
1727 behind which may be slightly odd from the user's point of view.
1732 if (_have_transaction) {
1736 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1737 i->view->region()->resume_property_changes ();
1741 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1745 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1747 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1752 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1755 // create a dummy marker for visual representation of moving the copy.
1756 // The actual copying is not done before we reach the finish callback.
1758 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1760 MeterMarker* new_marker = new MeterMarker (
1762 *_editor->meter_group,
1763 *_editor->cursor_group,
1764 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1766 *new MeterSection (_marker->meter())
1769 _item = &new_marker->the_item ();
1770 _marker = new_marker;
1774 MetricSection& section (_marker->meter());
1776 if (!section.movable()) {
1782 Drag::start_grab (event, cursor);
1784 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1786 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1790 MeterMarkerDrag::motion (GdkEvent* event, bool)
1792 framepos_t const pf = adjusted_current_frame (event);
1794 _marker->set_position (pf);
1796 _editor->show_verbose_time_cursor (pf, 10);
1800 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1802 if (!movement_occurred) {
1806 motion (event, false);
1810 TempoMap& map (_editor->session()->tempo_map());
1811 map.bbt_time (last_pointer_frame(), when);
1813 if (_copy == true) {
1814 _editor->begin_reversible_command (_("copy meter mark"));
1815 XMLNode &before = map.get_state();
1816 map.add_meter (_marker->meter(), when);
1817 XMLNode &after = map.get_state();
1818 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1819 _editor->commit_reversible_command ();
1821 // delete the dummy marker we used for visual representation of copying.
1822 // a new visual marker will show up automatically.
1825 _editor->begin_reversible_command (_("move meter mark"));
1826 XMLNode &before = map.get_state();
1827 map.move_meter (_marker->meter(), when);
1828 XMLNode &after = map.get_state();
1829 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1830 _editor->commit_reversible_command ();
1835 MeterMarkerDrag::aborted ()
1837 _marker->set_position (_marker->meter().frame ());
1840 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1844 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1846 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1851 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1856 // create a dummy marker for visual representation of moving the copy.
1857 // The actual copying is not done before we reach the finish callback.
1859 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1861 TempoMarker* new_marker = new TempoMarker (
1863 *_editor->tempo_group,
1864 *_editor->cursor_group,
1865 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1867 *new TempoSection (_marker->tempo())
1870 _item = &new_marker->the_item ();
1871 _marker = new_marker;
1875 MetricSection& section (_marker->tempo());
1877 if (!section.movable()) {
1882 Drag::start_grab (event, cursor);
1884 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1885 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1889 TempoMarkerDrag::motion (GdkEvent* event, bool)
1891 framepos_t const pf = adjusted_current_frame (event);
1892 _marker->set_position (pf);
1893 _editor->show_verbose_time_cursor (pf, 10);
1897 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1899 if (!movement_occurred) {
1903 motion (event, false);
1907 TempoMap& map (_editor->session()->tempo_map());
1908 map.bbt_time (last_pointer_frame(), when);
1910 if (_copy == true) {
1911 _editor->begin_reversible_command (_("copy tempo mark"));
1912 XMLNode &before = map.get_state();
1913 map.add_tempo (_marker->tempo(), when);
1914 XMLNode &after = map.get_state();
1915 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1916 _editor->commit_reversible_command ();
1918 // delete the dummy marker we used for visual representation of copying.
1919 // a new visual marker will show up automatically.
1922 _editor->begin_reversible_command (_("move tempo mark"));
1923 XMLNode &before = map.get_state();
1924 map.move_tempo (_marker->tempo(), when);
1925 XMLNode &after = map.get_state();
1926 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1927 _editor->commit_reversible_command ();
1932 TempoMarkerDrag::aborted ()
1934 _marker->set_position (_marker->tempo().frame());
1937 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1941 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
1943 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1948 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1950 Drag::start_grab (event, c);
1954 framepos_t where = _editor->event_frame (event, 0, 0);
1956 _editor->snap_to_with_modifier (where, event);
1957 _editor->playhead_cursor->set_position (where);
1961 if (_cursor == _editor->playhead_cursor) {
1962 _editor->_dragging_playhead = true;
1964 Session* s = _editor->session ();
1967 if (_was_rolling && _stop) {
1971 if (s->is_auditioning()) {
1972 s->cancel_audition ();
1975 s->request_suspend_timecode_transmission ();
1977 if (s->timecode_transmission_suspended ()) {
1978 framepos_t const f = _editor->playhead_cursor->current_frame;
1979 s->send_mmc_locate (f);
1980 s->send_full_time_code (f);
1985 _pointer_frame_offset = raw_grab_frame() - _cursor->current_frame;
1987 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1991 CursorDrag::motion (GdkEvent* event, bool)
1993 framepos_t const adjusted_frame = adjusted_current_frame (event);
1995 if (adjusted_frame == last_pointer_frame()) {
1999 _cursor->set_position (adjusted_frame);
2001 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2003 Session* s = _editor->session ();
2004 if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
2005 framepos_t const f = _editor->playhead_cursor->current_frame;
2006 s->send_mmc_locate (f);
2007 s->send_full_time_code (f);
2012 _editor->update_canvas_now ();
2014 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2018 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2020 _editor->_dragging_playhead = false;
2022 if (!movement_occurred && _stop) {
2026 motion (event, false);
2028 if (_item == &_editor->playhead_cursor->canvas_item) {
2029 Session* s = _editor->session ();
2031 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2032 _editor->_pending_locate_request = true;
2033 s->request_resume_timecode_transmission ();
2039 CursorDrag::aborted ()
2041 if (_editor->_dragging_playhead) {
2042 _editor->session()->request_resume_timecode_transmission ();
2043 _editor->_dragging_playhead = false;
2046 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2049 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2050 : RegionDrag (e, i, p, v)
2052 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2056 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2058 Drag::start_grab (event, cursor);
2060 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2061 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2063 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2064 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2066 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2070 FadeInDrag::motion (GdkEvent* event, bool)
2072 framecnt_t fade_length;
2074 framepos_t const pos = adjusted_current_frame (event);
2076 boost::shared_ptr<Region> region = _primary->region ();
2078 if (pos < (region->position() + 64)) {
2079 fade_length = 64; // this should be a minimum defined somewhere
2080 } else if (pos > region->last_frame()) {
2081 fade_length = region->length();
2083 fade_length = pos - region->position();
2086 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2088 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2094 tmp->reset_fade_in_shape_width (fade_length);
2095 tmp->show_fade_line((framecnt_t) fade_length);
2098 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2102 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2104 if (!movement_occurred) {
2108 framecnt_t fade_length;
2110 framepos_t const pos = adjusted_current_frame (event);
2112 boost::shared_ptr<Region> region = _primary->region ();
2114 if (pos < (region->position() + 64)) {
2115 fade_length = 64; // this should be a minimum defined somewhere
2116 } else if (pos > region->last_frame()) {
2117 fade_length = region->length();
2119 fade_length = pos - region->position();
2122 _editor->begin_reversible_command (_("change fade in length"));
2124 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2126 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2132 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2133 XMLNode &before = alist->get_state();
2135 tmp->audio_region()->set_fade_in_length (fade_length);
2136 tmp->audio_region()->set_fade_in_active (true);
2137 tmp->hide_fade_line();
2139 XMLNode &after = alist->get_state();
2140 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2143 _editor->commit_reversible_command ();
2147 FadeInDrag::aborted ()
2149 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2150 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2156 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2157 tmp->hide_fade_line();
2161 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2162 : RegionDrag (e, i, p, v)
2164 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2168 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2170 Drag::start_grab (event, cursor);
2172 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2173 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2175 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2176 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2178 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2182 FadeOutDrag::motion (GdkEvent* event, bool)
2184 framecnt_t fade_length;
2186 framepos_t const pos = adjusted_current_frame (event);
2188 boost::shared_ptr<Region> region = _primary->region ();
2190 if (pos > (region->last_frame() - 64)) {
2191 fade_length = 64; // this should really be a minimum fade defined somewhere
2193 else if (pos < region->position()) {
2194 fade_length = region->length();
2197 fade_length = region->last_frame() - pos;
2200 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2202 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2208 tmp->reset_fade_out_shape_width (fade_length);
2209 tmp->show_fade_line(region->length() - fade_length);
2212 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2216 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2218 if (!movement_occurred) {
2222 framecnt_t fade_length;
2224 framepos_t const pos = adjusted_current_frame (event);
2226 boost::shared_ptr<Region> region = _primary->region ();
2228 if (pos > (region->last_frame() - 64)) {
2229 fade_length = 64; // this should really be a minimum fade defined somewhere
2231 else if (pos < region->position()) {
2232 fade_length = region->length();
2235 fade_length = region->last_frame() - pos;
2238 _editor->begin_reversible_command (_("change fade out length"));
2240 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2242 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2248 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2249 XMLNode &before = alist->get_state();
2251 tmp->audio_region()->set_fade_out_length (fade_length);
2252 tmp->audio_region()->set_fade_out_active (true);
2253 tmp->hide_fade_line();
2255 XMLNode &after = alist->get_state();
2256 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2259 _editor->commit_reversible_command ();
2263 FadeOutDrag::aborted ()
2265 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2266 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2272 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2273 tmp->hide_fade_line();
2277 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2280 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2282 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2285 _points.push_back (Gnome::Art::Point (0, 0));
2286 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2288 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2289 _line->property_width_pixels() = 1;
2290 _line->property_points () = _points;
2293 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2296 MarkerDrag::~MarkerDrag ()
2298 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2304 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2306 Drag::start_grab (event, cursor);
2310 Location *location = _editor->find_location_from_marker (_marker, is_start);
2311 _editor->_dragging_edit_point = true;
2313 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2315 update_item (location);
2317 // _drag_line->show();
2318 // _line->raise_to_top();
2321 _editor->show_verbose_time_cursor (location->start(), 10);
2323 _editor->show_verbose_time_cursor (location->end(), 10);
2326 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2329 case Selection::Toggle:
2330 _editor->selection->toggle (_marker);
2332 case Selection::Set:
2333 if (!_editor->selection->selected (_marker)) {
2334 _editor->selection->set (_marker);
2337 case Selection::Extend:
2339 Locations::LocationList ll;
2340 list<Marker*> to_add;
2342 _editor->selection->markers.range (s, e);
2343 s = min (_marker->position(), s);
2344 e = max (_marker->position(), e);
2347 if (e < max_framepos) {
2350 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2351 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2352 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2355 to_add.push_back (lm->start);
2358 to_add.push_back (lm->end);
2362 if (!to_add.empty()) {
2363 _editor->selection->add (to_add);
2367 case Selection::Add:
2368 _editor->selection->add (_marker);
2372 /* Set up copies for us to manipulate during the drag */
2374 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2375 Location* l = _editor->find_location_from_marker (*i, is_start);
2376 _copied_locations.push_back (new Location (*l));
2381 MarkerDrag::motion (GdkEvent* event, bool)
2383 framecnt_t f_delta = 0;
2385 bool move_both = false;
2387 Location *real_location;
2388 Location *copy_location = 0;
2390 framepos_t const newframe = adjusted_current_frame (event);
2392 framepos_t next = newframe;
2394 if (newframe == last_pointer_frame()) {
2398 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2402 MarkerSelection::iterator i;
2403 list<Location*>::iterator x;
2405 /* find the marker we're dragging, and compute the delta */
2407 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2408 x != _copied_locations.end() && i != _editor->selection->markers.end();
2414 if (marker == _marker) {
2416 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2421 if (real_location->is_mark()) {
2422 f_delta = newframe - copy_location->start();
2426 switch (marker->type()) {
2427 case Marker::SessionStart:
2428 case Marker::RangeStart:
2429 case Marker::LoopStart:
2430 case Marker::PunchIn:
2431 f_delta = newframe - copy_location->start();
2434 case Marker::SessionEnd:
2435 case Marker::RangeEnd:
2436 case Marker::LoopEnd:
2437 case Marker::PunchOut:
2438 f_delta = newframe - copy_location->end();
2441 /* what kind of marker is this ? */
2449 if (i == _editor->selection->markers.end()) {
2450 /* hmm, impossible - we didn't find the dragged marker */
2454 /* now move them all */
2456 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2457 x != _copied_locations.end() && i != _editor->selection->markers.end();
2463 /* call this to find out if its the start or end */
2465 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2469 if (real_location->locked()) {
2473 if (copy_location->is_mark()) {
2477 copy_location->set_start (copy_location->start() + f_delta);
2481 framepos_t new_start = copy_location->start() + f_delta;
2482 framepos_t new_end = copy_location->end() + f_delta;
2484 if (is_start) { // start-of-range marker
2487 copy_location->set_start (new_start);
2488 copy_location->set_end (new_end);
2489 } else if (new_start < copy_location->end()) {
2490 copy_location->set_start (new_start);
2492 _editor->snap_to (next, 1, true);
2493 copy_location->set_end (next);
2494 copy_location->set_start (newframe);
2497 } else { // end marker
2500 copy_location->set_end (new_end);
2501 copy_location->set_start (new_start);
2502 } else if (new_end > copy_location->start()) {
2503 copy_location->set_end (new_end);
2504 } else if (newframe > 0) {
2505 _editor->snap_to (next, -1, true);
2506 copy_location->set_start (next);
2507 copy_location->set_end (newframe);
2512 update_item (copy_location);
2514 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2517 lm->set_position (copy_location->start(), copy_location->end());
2521 assert (!_copied_locations.empty());
2523 _editor->show_verbose_time_cursor (newframe, 10);
2526 _editor->update_canvas_now ();
2531 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2533 if (!movement_occurred) {
2535 /* just a click, do nothing but finish
2536 off the selection process
2539 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2542 case Selection::Set:
2543 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2544 _editor->selection->set (_marker);
2548 case Selection::Toggle:
2549 case Selection::Extend:
2550 case Selection::Add:
2557 _editor->_dragging_edit_point = false;
2559 _editor->begin_reversible_command ( _("move marker") );
2560 XMLNode &before = _editor->session()->locations()->get_state();
2562 MarkerSelection::iterator i;
2563 list<Location*>::iterator x;
2566 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2567 x != _copied_locations.end() && i != _editor->selection->markers.end();
2570 Location * location = _editor->find_location_from_marker (*i, is_start);
2574 if (location->locked()) {
2578 if (location->is_mark()) {
2579 location->set_start ((*x)->start());
2581 location->set ((*x)->start(), (*x)->end());
2586 XMLNode &after = _editor->session()->locations()->get_state();
2587 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2588 _editor->commit_reversible_command ();
2594 MarkerDrag::aborted ()
2600 MarkerDrag::update_item (Location* location)
2602 double const x1 = _editor->frame_to_pixel (location->start());
2604 _points.front().set_x(x1);
2605 _points.back().set_x(x1);
2606 _line->property_points() = _points;
2609 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2611 _cumulative_x_drag (0),
2612 _cumulative_y_drag (0)
2614 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2616 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2622 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2624 Drag::start_grab (event, _editor->fader_cursor);
2626 // start the grab at the center of the control point so
2627 // the point doesn't 'jump' to the mouse after the first drag
2628 _fixed_grab_x = _point->get_x();
2629 _fixed_grab_y = _point->get_y();
2631 float const fraction = 1 - (_point->get_y() / _point->line().height());
2633 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2635 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2636 event->button.x + 10, event->button.y + 10);
2638 _editor->show_verbose_canvas_cursor ();
2642 ControlPointDrag::motion (GdkEvent* event, bool)
2644 double dx = _drags->current_pointer_x() - last_pointer_x();
2645 double dy = _drags->current_pointer_y() - last_pointer_y();
2647 if (event->button.state & Keyboard::SecondaryModifier) {
2652 /* coordinate in pixels relative to the start of the region (for region-based automation)
2653 or track (for track-based automation) */
2654 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2655 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2657 // calculate zero crossing point. back off by .01 to stay on the
2658 // positive side of zero
2659 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2661 // make sure we hit zero when passing through
2662 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2666 if (_x_constrained) {
2669 if (_y_constrained) {
2673 _cumulative_x_drag = cx - _fixed_grab_x;
2674 _cumulative_y_drag = cy - _fixed_grab_y;
2678 cy = min ((double) _point->line().height(), cy);
2680 framepos_t cx_frames = _editor->unit_to_frame (cx);
2682 if (!_x_constrained) {
2683 _editor->snap_to_with_modifier (cx_frames, event);
2686 cx_frames = min (cx_frames, _point->line().maximum_time());
2688 float const fraction = 1.0 - (cy / _point->line().height());
2690 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2692 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2694 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2698 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2700 if (!movement_occurred) {
2704 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2705 _editor->reset_point_selection ();
2709 motion (event, false);
2712 _point->line().end_drag ();
2713 _editor->session()->commit_reversible_command ();
2717 ControlPointDrag::aborted ()
2719 _point->line().reset ();
2723 ControlPointDrag::active (Editing::MouseMode m)
2725 if (m == Editing::MouseGain) {
2726 /* always active in mouse gain */
2730 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2731 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2734 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2737 _cumulative_y_drag (0)
2739 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2743 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2745 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2748 _item = &_line->grab_item ();
2750 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2751 origin, and ditto for y.
2754 double cx = event->button.x;
2755 double cy = event->button.y;
2757 _line->parent_group().w2i (cx, cy);
2759 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2764 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2765 /* no adjacent points */
2769 Drag::start_grab (event, _editor->fader_cursor);
2771 /* store grab start in parent frame */
2776 double fraction = 1.0 - (cy / _line->height());
2778 _line->start_drag_line (before, after, fraction);
2780 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2781 event->button.x + 10, event->button.y + 10);
2783 _editor->show_verbose_canvas_cursor ();
2787 LineDrag::motion (GdkEvent* event, bool)
2789 double dy = _drags->current_pointer_y() - last_pointer_y();
2791 if (event->button.state & Keyboard::SecondaryModifier) {
2795 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2797 _cumulative_y_drag = cy - _fixed_grab_y;
2800 cy = min ((double) _line->height(), cy);
2802 double const fraction = 1.0 - (cy / _line->height());
2806 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2812 /* we are ignoring x position for this drag, so we can just pass in anything */
2813 _line->drag_motion (0, fraction, true, push);
2815 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2819 LineDrag::finished (GdkEvent* event, bool)
2821 motion (event, false);
2823 _editor->session()->commit_reversible_command ();
2827 LineDrag::aborted ()
2832 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2835 _cumulative_x_drag (0)
2837 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2841 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2843 Drag::start_grab (event);
2845 _line = reinterpret_cast<SimpleLine*> (_item);
2848 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2850 double cx = event->button.x;
2851 double cy = event->button.y;
2853 _item->property_parent().get_value()->w2i(cx, cy);
2855 /* store grab start in parent frame */
2856 _region_view_grab_x = cx;
2858 _before = _line->property_x1();
2860 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2862 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2866 FeatureLineDrag::motion (GdkEvent* event, bool)
2868 double dx = _drags->current_pointer_x() - last_pointer_x();
2870 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2872 _cumulative_x_drag += dx;
2874 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2883 _line->property_x1() = cx;
2884 _line->property_x2() = cx;
2886 _before = _line->property_x1();
2890 FeatureLineDrag::finished (GdkEvent* event, bool)
2892 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2893 _arv->update_transient(_before, _line->property_x1());
2897 FeatureLineDrag::aborted ()
2902 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2905 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2909 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2911 Drag::start_grab (event);
2912 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2916 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2923 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2925 framepos_t grab = grab_frame ();
2926 if (Config->get_rubberbanding_snaps_to_grid ()) {
2927 _editor->snap_to_with_modifier (grab, event);
2930 /* base start and end on initial click position */
2940 if (_drags->current_pointer_y() < grab_y()) {
2941 y1 = _drags->current_pointer_y();
2944 y2 = _drags->current_pointer_y();
2949 if (start != end || y1 != y2) {
2951 double x1 = _editor->frame_to_pixel (start);
2952 double x2 = _editor->frame_to_pixel (end);
2954 _editor->rubberband_rect->property_x1() = x1;
2955 _editor->rubberband_rect->property_y1() = y1;
2956 _editor->rubberband_rect->property_x2() = x2;
2957 _editor->rubberband_rect->property_y2() = y2;
2959 _editor->rubberband_rect->show();
2960 _editor->rubberband_rect->raise_to_top();
2962 _editor->show_verbose_time_cursor (pf, 10);
2967 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2969 if (movement_occurred) {
2971 motion (event, false);
2974 if (_drags->current_pointer_y() < grab_y()) {
2975 y1 = _drags->current_pointer_y();
2978 y2 = _drags->current_pointer_y();
2983 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2986 _editor->begin_reversible_command (_("rubberband selection"));
2988 if (grab_frame() < last_pointer_frame()) {
2989 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
2991 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
2995 _editor->commit_reversible_command ();
2999 if (!getenv("ARDOUR_SAE")) {
3000 _editor->selection->clear_tracks();
3002 _editor->selection->clear_regions();
3003 _editor->selection->clear_points ();
3004 _editor->selection->clear_lines ();
3007 _editor->rubberband_rect->hide();
3011 RubberbandSelectDrag::aborted ()
3013 _editor->rubberband_rect->hide ();
3016 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3017 : RegionDrag (e, i, p, v)
3019 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3023 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3025 Drag::start_grab (event, cursor);
3027 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3031 TimeFXDrag::motion (GdkEvent* event, bool)
3033 RegionView* rv = _primary;
3035 framepos_t const pf = adjusted_current_frame (event);
3037 if (pf > rv->region()->position()) {
3038 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3041 _editor->show_verbose_time_cursor (pf, 10);
3045 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3047 _primary->get_time_axis_view().hide_timestretch ();
3049 if (!movement_occurred) {
3053 if (last_pointer_frame() < _primary->region()->position()) {
3054 /* backwards drag of the left edge - not usable */
3058 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3060 float percentage = (double) newlen / (double) _primary->region()->length();
3062 #ifndef USE_RUBBERBAND
3063 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3064 if (_primary->region()->data_type() == DataType::AUDIO) {
3065 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3069 _editor->begin_reversible_command (_("timestretch"));
3071 // XXX how do timeFX on multiple regions ?
3076 if (_editor->time_stretch (rs, percentage) == -1) {
3077 error << _("An error occurred while executing time stretch operation") << endmsg;
3082 TimeFXDrag::aborted ()
3084 _primary->get_time_axis_view().hide_timestretch ();
3087 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3090 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3094 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3096 Drag::start_grab (event);
3100 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3102 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3106 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3108 if (movement_occurred && _editor->session()) {
3109 /* make sure we stop */
3110 _editor->session()->request_transport_speed (0.0);
3115 ScrubDrag::aborted ()
3120 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3124 , _original_pointer_time_axis (-1)
3125 , _last_pointer_time_axis (-1)
3127 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3131 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3133 framepos_t start = 0;
3136 if (_editor->session() == 0) {
3140 Gdk::Cursor* cursor = 0;
3142 switch (_operation) {
3143 case CreateSelection:
3144 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3149 cursor = _editor->selector_cursor;
3150 Drag::start_grab (event, cursor);
3153 case SelectionStartTrim:
3154 if (_editor->clicked_axisview) {
3155 _editor->clicked_axisview->order_selection_trims (_item, true);
3157 Drag::start_grab (event, _editor->left_side_trim_cursor);
3158 start = _editor->selection->time[_editor->clicked_selection].start;
3159 _pointer_frame_offset = raw_grab_frame() - start;
3162 case SelectionEndTrim:
3163 if (_editor->clicked_axisview) {
3164 _editor->clicked_axisview->order_selection_trims (_item, false);
3166 Drag::start_grab (event, _editor->right_side_trim_cursor);
3167 end = _editor->selection->time[_editor->clicked_selection].end;
3168 _pointer_frame_offset = raw_grab_frame() - end;
3172 start = _editor->selection->time[_editor->clicked_selection].start;
3173 Drag::start_grab (event, cursor);
3174 _pointer_frame_offset = raw_grab_frame() - start;
3178 if (_operation == SelectionMove) {
3179 _editor->show_verbose_time_cursor (start, 10);
3181 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3184 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3188 SelectionDrag::motion (GdkEvent* event, bool first_move)
3190 framepos_t start = 0;
3194 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3195 if (pending_time_axis.first == 0) {
3199 framepos_t const pending_position = adjusted_current_frame (event);
3201 /* only alter selection if things have changed */
3203 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3207 switch (_operation) {
3208 case CreateSelection:
3210 framepos_t grab = grab_frame ();
3213 _editor->snap_to (grab);
3216 if (pending_position < grab_frame()) {
3217 start = pending_position;
3220 end = pending_position;
3224 /* first drag: Either add to the selection
3225 or create a new selection
3231 /* adding to the selection */
3232 _editor->set_selected_track_as_side_effect (Selection::Add);
3233 //_editor->selection->add (_editor->clicked_axisview);
3234 _editor->clicked_selection = _editor->selection->add (start, end);
3239 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3240 //_editor->selection->set (_editor->clicked_axisview);
3241 _editor->set_selected_track_as_side_effect (Selection::Set);
3244 _editor->clicked_selection = _editor->selection->set (start, end);
3248 /* select the track that we're in */
3249 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3250 // _editor->set_selected_track_as_side_effect (Selection::Add);
3251 _editor->selection->add (pending_time_axis.first);
3252 _added_time_axes.push_back (pending_time_axis.first);
3255 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3256 tracks that we selected in the first place.
3259 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3260 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3262 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3263 while (i != _added_time_axes.end()) {
3265 list<TimeAxisView*>::iterator tmp = i;
3268 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3269 _editor->selection->remove (*i);
3270 _added_time_axes.remove (*i);
3279 case SelectionStartTrim:
3281 start = _editor->selection->time[_editor->clicked_selection].start;
3282 end = _editor->selection->time[_editor->clicked_selection].end;
3284 if (pending_position > end) {
3287 start = pending_position;
3291 case SelectionEndTrim:
3293 start = _editor->selection->time[_editor->clicked_selection].start;
3294 end = _editor->selection->time[_editor->clicked_selection].end;
3296 if (pending_position < start) {
3299 end = pending_position;
3306 start = _editor->selection->time[_editor->clicked_selection].start;
3307 end = _editor->selection->time[_editor->clicked_selection].end;
3309 length = end - start;
3311 start = pending_position;
3312 _editor->snap_to (start);
3314 end = start + length;
3319 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3320 _editor->start_canvas_autoscroll (1, 0);
3324 _editor->selection->replace (_editor->clicked_selection, start, end);
3327 if (_operation == SelectionMove) {
3328 _editor->show_verbose_time_cursor(start, 10);
3330 _editor->show_verbose_time_cursor(pending_position, 10);
3335 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3337 Session* s = _editor->session();
3339 if (movement_occurred) {
3340 motion (event, false);
3341 /* XXX this is not object-oriented programming at all. ick */
3342 if (_editor->selection->time.consolidate()) {
3343 _editor->selection->TimeChanged ();
3346 /* XXX what if its a music time selection? */
3347 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3348 s->request_play_range (&_editor->selection->time, true);
3353 /* just a click, no pointer movement.*/
3355 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3356 _editor->selection->clear_time();
3359 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3360 _editor->selection->set (_editor->clicked_axisview);
3363 if (s && s->get_play_range () && s->transport_rolling()) {
3364 s->request_stop (false, false);
3369 _editor->stop_canvas_autoscroll ();
3373 SelectionDrag::aborted ()
3378 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3383 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3385 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3386 physical_screen_height (_editor->get_window()));
3387 _drag_rect->hide ();
3389 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3390 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3394 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3396 if (_editor->session() == 0) {
3400 Gdk::Cursor* cursor = 0;
3402 if (!_editor->temp_location) {
3403 _editor->temp_location = new Location (*_editor->session());
3406 switch (_operation) {
3407 case CreateRangeMarker:
3408 case CreateTransportMarker:
3409 case CreateCDMarker:
3411 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3416 cursor = _editor->selector_cursor;
3420 Drag::start_grab (event, cursor);
3422 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3426 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3428 framepos_t start = 0;
3430 ArdourCanvas::SimpleRect *crect;
3432 switch (_operation) {
3433 case CreateRangeMarker:
3434 crect = _editor->range_bar_drag_rect;
3436 case CreateTransportMarker:
3437 crect = _editor->transport_bar_drag_rect;
3439 case CreateCDMarker:
3440 crect = _editor->cd_marker_bar_drag_rect;
3443 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3448 framepos_t const pf = adjusted_current_frame (event);
3450 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3451 framepos_t grab = grab_frame ();
3452 _editor->snap_to (grab);
3454 if (pf < grab_frame()) {
3462 /* first drag: Either add to the selection
3463 or create a new selection.
3468 _editor->temp_location->set (start, end);
3472 update_item (_editor->temp_location);
3474 //_drag_rect->raise_to_top();
3479 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3480 _editor->start_canvas_autoscroll (1, 0);
3484 _editor->temp_location->set (start, end);
3486 double x1 = _editor->frame_to_pixel (start);
3487 double x2 = _editor->frame_to_pixel (end);
3488 crect->property_x1() = x1;
3489 crect->property_x2() = x2;
3491 update_item (_editor->temp_location);
3494 _editor->show_verbose_time_cursor (pf, 10);
3499 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3501 Location * newloc = 0;
3505 if (movement_occurred) {
3506 motion (event, false);
3509 switch (_operation) {
3510 case CreateRangeMarker:
3511 case CreateCDMarker:
3513 _editor->begin_reversible_command (_("new range marker"));
3514 XMLNode &before = _editor->session()->locations()->get_state();
3515 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3516 if (_operation == CreateCDMarker) {
3517 flags = Location::IsRangeMarker | Location::IsCDMarker;
3518 _editor->cd_marker_bar_drag_rect->hide();
3521 flags = Location::IsRangeMarker;
3522 _editor->range_bar_drag_rect->hide();
3524 newloc = new Location (
3525 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3528 _editor->session()->locations()->add (newloc, true);
3529 XMLNode &after = _editor->session()->locations()->get_state();
3530 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3531 _editor->commit_reversible_command ();
3535 case CreateTransportMarker:
3536 // popup menu to pick loop or punch
3537 _editor->new_transport_marker_context_menu (&event->button, _item);
3541 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3543 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3548 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3550 if (end == max_framepos) {
3551 end = _editor->session()->current_end_frame ();
3554 if (start == max_framepos) {
3555 start = _editor->session()->current_start_frame ();
3558 switch (_editor->mouse_mode) {
3560 /* find the two markers on either side and then make the selection from it */
3561 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3565 /* find the two markers on either side of the click and make the range out of it */
3566 _editor->selection->set (start, end);
3575 _editor->stop_canvas_autoscroll ();
3579 RangeMarkerBarDrag::aborted ()
3585 RangeMarkerBarDrag::update_item (Location* location)
3587 double const x1 = _editor->frame_to_pixel (location->start());
3588 double const x2 = _editor->frame_to_pixel (location->end());
3590 _drag_rect->property_x1() = x1;
3591 _drag_rect->property_x2() = x2;
3594 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3598 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3602 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3604 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3605 Drag::start_grab (event, _editor->zoom_out_cursor);
3608 Drag::start_grab (event, _editor->zoom_in_cursor);
3612 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3616 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3621 framepos_t const pf = adjusted_current_frame (event);
3623 framepos_t grab = grab_frame ();
3624 _editor->snap_to_with_modifier (grab, event);
3626 /* base start and end on initial click position */
3638 _editor->zoom_rect->show();
3639 _editor->zoom_rect->raise_to_top();
3642 _editor->reposition_zoom_rect(start, end);
3644 _editor->show_verbose_time_cursor (pf, 10);
3649 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3651 if (movement_occurred) {
3652 motion (event, false);
3654 if (grab_frame() < last_pointer_frame()) {
3655 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3657 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3660 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3663 _editor->zoom_rect->hide();
3667 MouseZoomDrag::aborted ()
3669 _editor->zoom_rect->hide ();
3672 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3674 , _cumulative_dx (0)
3675 , _cumulative_dy (0)
3677 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3679 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3680 _region = &_primary->region_view ();
3681 _note_height = _region->midi_stream_view()->note_height ();
3685 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3687 Drag::start_grab (event);
3689 if (!(_was_selected = _primary->selected())) {
3691 /* tertiary-click means extend selection - we'll do that on button release,
3692 so don't add it here, because otherwise we make it hard to figure
3693 out the "extend-to" range.
3696 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3699 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3702 _region->note_selected (_primary, true);
3704 _region->unique_select (_primary);
3710 /** @return Current total drag x change in frames */
3712 NoteDrag::total_dx () const
3715 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3717 /* primary note time */
3718 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3720 /* new time of the primary note relative to the region position */
3721 frameoffset_t const st = n + dx;
3723 /* snap and return corresponding delta */
3724 return _region->snap_frame_to_frame (st) - n;
3727 /** @return Current total drag y change in notes */
3729 NoteDrag::total_dy () const
3731 /* this is `backwards' to make increasing note number go in the right direction */
3732 double const dy = _drags->current_pointer_y() - grab_y();
3737 if (abs (dy) >= _note_height) {
3739 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3741 ndy = (int8_t) floor (dy / _note_height / 2.0);
3745 /* more positive value = higher pitch and higher y-axis position on track,
3746 which is the inverse of the X-centric geometric universe
3753 NoteDrag::motion (GdkEvent *, bool)
3755 /* Total change in x and y since the start of the drag */
3756 frameoffset_t const dx = total_dx ();
3757 int8_t const dy = -total_dy ();
3759 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3760 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3761 double const tdy = dy * _note_height - _cumulative_dy;
3764 _cumulative_dx += tdx;
3765 _cumulative_dy += tdy;
3767 int8_t note_delta = total_dy();
3769 _region->move_selection (tdx, tdy, note_delta);
3772 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3773 (int) floor (_primary->note()->note() + note_delta));
3775 _editor->show_verbose_canvas_cursor_with (buf);
3780 NoteDrag::finished (GdkEvent* ev, bool moved)
3783 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3785 if (_was_selected) {
3786 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3788 _region->note_deselected (_primary);
3791 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3792 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3794 if (!extend && !add && _region->selection_size() > 1) {
3795 _region->unique_select (_primary);
3796 } else if (extend) {
3797 _region->note_selected (_primary, true, true);
3799 /* it was added during button press */
3804 _region->note_dropped (_primary, total_dx(), total_dy());
3809 NoteDrag::aborted ()
3814 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3815 : Drag (editor, item)
3817 , _nothing_to_drag (false)
3819 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3821 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3824 /* get all lines in the automation view */
3825 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3827 /* find those that overlap the ranges being dragged */
3828 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3829 while (i != lines.end ()) {
3830 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3833 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3835 /* check this range against all the AudioRanges that we are using */
3836 list<AudioRange>::const_iterator k = _ranges.begin ();
3837 while (k != _ranges.end()) {
3838 if (k->coverage (r.first, r.second) != OverlapNone) {
3844 /* add it to our list if it overlaps at all */
3845 if (k != _ranges.end()) {
3850 _lines.push_back (n);
3856 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3860 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3862 Drag::start_grab (event, cursor);
3864 /* Get line states before we start changing things */
3865 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3866 i->state = &i->line->get_state ();
3869 if (_ranges.empty()) {
3871 /* No selected time ranges: drag all points */
3872 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3873 uint32_t const N = i->line->npoints ();
3874 for (uint32_t j = 0; j < N; ++j) {
3875 i->points.push_back (i->line->nth (j));
3881 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3883 framecnt_t const half = (i->start + i->end) / 2;
3885 /* find the line that this audio range starts in */
3886 list<Line>::iterator j = _lines.begin();
3887 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3891 if (j != _lines.end()) {
3892 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3894 /* j is the line that this audio range starts in; fade into it;
3895 64 samples length plucked out of thin air.
3898 framepos_t a = i->start + 64;
3903 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3904 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3906 the_list->add (p, the_list->eval (p));
3907 j->line->add_always_in_view (p);
3908 the_list->add (q, the_list->eval (q));
3909 j->line->add_always_in_view (q);
3912 /* same thing for the end */
3915 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3919 if (j != _lines.end()) {
3920 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3922 /* j is the line that this audio range starts in; fade out of it;
3923 64 samples length plucked out of thin air.
3926 framepos_t b = i->end - 64;
3931 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3932 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
3934 the_list->add (p, the_list->eval (p));
3935 j->line->add_always_in_view (p);
3936 the_list->add (q, the_list->eval (q));
3937 j->line->add_always_in_view (q);
3941 _nothing_to_drag = true;
3943 /* Find all the points that should be dragged and put them in the relevant
3944 points lists in the Line structs.
3947 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3949 uint32_t const N = i->line->npoints ();
3950 for (uint32_t j = 0; j < N; ++j) {
3952 /* here's a control point on this line */
3953 ControlPoint* p = i->line->nth (j);
3954 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
3956 /* see if it's inside a range */
3957 list<AudioRange>::const_iterator k = _ranges.begin ();
3958 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
3962 if (k != _ranges.end()) {
3963 /* dragging this point */
3964 _nothing_to_drag = false;
3965 i->points.push_back (p);
3971 if (_nothing_to_drag) {
3975 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3976 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
3981 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3983 if (_nothing_to_drag) {
3987 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3988 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
3990 /* we are ignoring x position for this drag, so we can just pass in anything */
3991 i->line->drag_motion (0, f, true, false);
3996 AutomationRangeDrag::finished (GdkEvent* event, bool)
3998 if (_nothing_to_drag) {
4002 motion (event, false);
4003 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4004 i->line->end_drag ();
4005 i->line->clear_always_in_view ();
4008 _editor->session()->commit_reversible_command ();
4012 AutomationRangeDrag::aborted ()
4014 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4015 i->line->clear_always_in_view ();
4020 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4023 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4024 layer = v->region()->layer ();
4025 initial_y = v->get_canvas_group()->property_y ();
4026 initial_playlist = v->region()->playlist ();
4027 initial_position = v->region()->position ();
4028 initial_end = v->region()->position () + v->region()->length ();