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;
1571 /* snap modifier works differently here..
1572 its current state has to be passed to the
1573 various trim functions in order to work properly
1577 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1578 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1579 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1581 if (tv && tv->is_track()) {
1582 speed = tv->track()->speed();
1585 framepos_t const pf = adjusted_current_frame (event);
1591 switch (_operation) {
1593 trim_type = "Region start trim";
1596 trim_type = "Region end trim";
1599 trim_type = "Region content trim";
1603 _editor->begin_reversible_command (trim_type);
1604 _have_transaction = true;
1606 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1607 RegionView* rv = i->view;
1608 rv->fake_set_opaque(false);
1609 rv->enable_display (false);
1610 rv->region()->clear_changes ();
1612 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1615 arv->temporarily_hide_envelope ();
1618 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1619 insert_result = _editor->motion_frozen_playlists.insert (pl);
1621 if (insert_result.second) {
1627 bool non_overlap_trim = false;
1629 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1630 non_overlap_trim = true;
1633 switch (_operation) {
1635 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1636 _editor->single_start_trim (*i->view, pf, non_overlap_trim);
1641 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1642 _editor->single_end_trim (*i->view, pf, non_overlap_trim);
1648 bool swap_direction = false;
1650 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1651 swap_direction = true;
1654 framecnt_t frame_delta = 0;
1656 bool left_direction = false;
1657 if (last_pointer_frame() > pf) {
1658 left_direction = true;
1661 if (left_direction) {
1662 frame_delta = (last_pointer_frame() - pf);
1664 frame_delta = (pf - last_pointer_frame());
1667 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1668 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1674 switch (_operation) {
1676 _editor->show_verbose_time_cursor((framepos_t) (rv->region()->position()/speed), 10);
1679 _editor->show_verbose_time_cursor((framepos_t) (rv->region()->last_frame()/speed), 10);
1682 _editor->show_verbose_time_cursor (pf, 10);
1689 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1691 if (movement_occurred) {
1692 motion (event, false);
1694 if (!_editor->selection->selected (_primary)) {
1695 _editor->thaw_region_after_trim (*_primary);
1698 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1699 _editor->thaw_region_after_trim (*i->view);
1700 i->view->enable_display (true);
1701 i->view->fake_set_opaque (true);
1702 if (_have_transaction) {
1703 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1707 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1711 _editor->motion_frozen_playlists.clear ();
1713 if (_have_transaction) {
1714 _editor->commit_reversible_command();
1718 /* no mouse movement */
1719 _editor->point_trim (event, adjusted_current_frame (event));
1722 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1723 i->view->region()->resume_property_changes ();
1728 TrimDrag::aborted ()
1730 /* Our motion method is changing model state, so use the Undo system
1731 to cancel. Perhaps not ideal, as this will leave an Undo point
1732 behind which may be slightly odd from the user's point of view.
1737 if (_have_transaction) {
1741 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1742 i->view->region()->resume_property_changes ();
1746 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1750 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1752 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1757 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1760 // create a dummy marker for visual representation of moving the copy.
1761 // The actual copying is not done before we reach the finish callback.
1763 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1764 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1765 *new MeterSection (_marker->meter()));
1767 _item = &new_marker->the_item ();
1768 _marker = new_marker;
1772 MetricSection& section (_marker->meter());
1774 if (!section.movable()) {
1780 Drag::start_grab (event, cursor);
1782 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1784 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1788 MeterMarkerDrag::motion (GdkEvent* event, bool)
1790 framepos_t const pf = adjusted_current_frame (event);
1792 _marker->set_position (pf);
1794 _editor->show_verbose_time_cursor (pf, 10);
1798 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1800 if (!movement_occurred) {
1804 motion (event, false);
1808 TempoMap& map (_editor->session()->tempo_map());
1809 map.bbt_time (last_pointer_frame(), when);
1811 if (_copy == true) {
1812 _editor->begin_reversible_command (_("copy meter mark"));
1813 XMLNode &before = map.get_state();
1814 map.add_meter (_marker->meter(), when);
1815 XMLNode &after = map.get_state();
1816 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1817 _editor->commit_reversible_command ();
1819 // delete the dummy marker we used for visual representation of copying.
1820 // a new visual marker will show up automatically.
1823 _editor->begin_reversible_command (_("move meter mark"));
1824 XMLNode &before = map.get_state();
1825 map.move_meter (_marker->meter(), when);
1826 XMLNode &after = map.get_state();
1827 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1828 _editor->commit_reversible_command ();
1833 MeterMarkerDrag::aborted ()
1835 _marker->set_position (_marker->meter().frame ());
1838 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1842 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1844 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1849 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1854 // create a dummy marker for visual representation of moving the copy.
1855 // The actual copying is not done before we reach the finish callback.
1857 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1858 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
1859 *new TempoSection (_marker->tempo()));
1861 _item = &new_marker->the_item ();
1862 _marker = new_marker;
1866 MetricSection& section (_marker->tempo());
1868 if (!section.movable()) {
1873 Drag::start_grab (event, cursor);
1875 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1876 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1880 TempoMarkerDrag::motion (GdkEvent* event, bool)
1882 framepos_t const pf = adjusted_current_frame (event);
1883 _marker->set_position (pf);
1884 _editor->show_verbose_time_cursor (pf, 10);
1888 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1890 if (!movement_occurred) {
1894 motion (event, false);
1898 TempoMap& map (_editor->session()->tempo_map());
1899 map.bbt_time (last_pointer_frame(), when);
1901 if (_copy == true) {
1902 _editor->begin_reversible_command (_("copy tempo mark"));
1903 XMLNode &before = map.get_state();
1904 map.add_tempo (_marker->tempo(), when);
1905 XMLNode &after = map.get_state();
1906 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1907 _editor->commit_reversible_command ();
1909 // delete the dummy marker we used for visual representation of copying.
1910 // a new visual marker will show up automatically.
1913 _editor->begin_reversible_command (_("move tempo mark"));
1914 XMLNode &before = map.get_state();
1915 map.move_tempo (_marker->tempo(), when);
1916 XMLNode &after = map.get_state();
1917 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1918 _editor->commit_reversible_command ();
1923 TempoMarkerDrag::aborted ()
1925 _marker->set_position (_marker->tempo().frame());
1928 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1932 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
1934 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1939 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1941 Drag::start_grab (event, c);
1945 framepos_t where = _editor->event_frame (event, 0, 0);
1947 _editor->snap_to_with_modifier (where, event);
1948 _editor->playhead_cursor->set_position (where);
1952 if (_cursor == _editor->playhead_cursor) {
1953 _editor->_dragging_playhead = true;
1955 Session* s = _editor->session ();
1958 if (_was_rolling && _stop) {
1962 if (s->is_auditioning()) {
1963 s->cancel_audition ();
1966 s->request_suspend_timecode_transmission ();
1968 if (s->timecode_transmission_suspended ()) {
1969 framepos_t const f = _editor->playhead_cursor->current_frame;
1970 s->send_mmc_locate (f);
1971 s->send_full_time_code (f);
1976 _pointer_frame_offset = raw_grab_frame() - _cursor->current_frame;
1978 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1982 CursorDrag::motion (GdkEvent* event, bool)
1984 framepos_t const adjusted_frame = adjusted_current_frame (event);
1986 if (adjusted_frame == last_pointer_frame()) {
1990 _cursor->set_position (adjusted_frame);
1992 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1994 Session* s = _editor->session ();
1995 if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
1996 framepos_t const f = _editor->playhead_cursor->current_frame;
1997 s->send_mmc_locate (f);
1998 s->send_full_time_code (f);
2003 _editor->update_canvas_now ();
2005 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2009 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2011 _editor->_dragging_playhead = false;
2013 if (!movement_occurred && _stop) {
2017 motion (event, false);
2019 if (_item == &_editor->playhead_cursor->canvas_item) {
2020 Session* s = _editor->session ();
2022 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2023 _editor->_pending_locate_request = true;
2024 s->request_resume_timecode_transmission ();
2030 CursorDrag::aborted ()
2032 if (_editor->_dragging_playhead) {
2033 _editor->session()->request_resume_timecode_transmission ();
2034 _editor->_dragging_playhead = false;
2037 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2040 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2041 : RegionDrag (e, i, p, v)
2043 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2047 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2049 Drag::start_grab (event, cursor);
2051 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2052 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2054 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2055 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2057 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2061 FadeInDrag::motion (GdkEvent* event, bool)
2063 framecnt_t fade_length;
2065 framepos_t const pos = adjusted_current_frame (event);
2067 boost::shared_ptr<Region> region = _primary->region ();
2069 if (pos < (region->position() + 64)) {
2070 fade_length = 64; // this should be a minimum defined somewhere
2071 } else if (pos > region->last_frame()) {
2072 fade_length = region->length();
2074 fade_length = pos - region->position();
2077 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2079 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2085 tmp->reset_fade_in_shape_width (fade_length);
2086 tmp->show_fade_line((framecnt_t) fade_length);
2089 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2093 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2095 if (!movement_occurred) {
2099 framecnt_t fade_length;
2101 framepos_t const pos = adjusted_current_frame (event);
2103 boost::shared_ptr<Region> region = _primary->region ();
2105 if (pos < (region->position() + 64)) {
2106 fade_length = 64; // this should be a minimum defined somewhere
2107 } else if (pos > region->last_frame()) {
2108 fade_length = region->length();
2110 fade_length = pos - region->position();
2113 _editor->begin_reversible_command (_("change fade in length"));
2115 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2117 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2123 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2124 XMLNode &before = alist->get_state();
2126 tmp->audio_region()->set_fade_in_length (fade_length);
2127 tmp->audio_region()->set_fade_in_active (true);
2128 tmp->hide_fade_line();
2130 XMLNode &after = alist->get_state();
2131 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2134 _editor->commit_reversible_command ();
2138 FadeInDrag::aborted ()
2140 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2141 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2147 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2148 tmp->hide_fade_line();
2152 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2153 : RegionDrag (e, i, p, v)
2155 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2159 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2161 Drag::start_grab (event, cursor);
2163 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2164 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2166 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2167 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2169 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2173 FadeOutDrag::motion (GdkEvent* event, bool)
2175 framecnt_t fade_length;
2177 framepos_t const pos = adjusted_current_frame (event);
2179 boost::shared_ptr<Region> region = _primary->region ();
2181 if (pos > (region->last_frame() - 64)) {
2182 fade_length = 64; // this should really be a minimum fade defined somewhere
2184 else if (pos < region->position()) {
2185 fade_length = region->length();
2188 fade_length = region->last_frame() - pos;
2191 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2193 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2199 tmp->reset_fade_out_shape_width (fade_length);
2200 tmp->show_fade_line(region->length() - fade_length);
2203 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2207 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2209 if (!movement_occurred) {
2213 framecnt_t fade_length;
2215 framepos_t const pos = adjusted_current_frame (event);
2217 boost::shared_ptr<Region> region = _primary->region ();
2219 if (pos > (region->last_frame() - 64)) {
2220 fade_length = 64; // this should really be a minimum fade defined somewhere
2222 else if (pos < region->position()) {
2223 fade_length = region->length();
2226 fade_length = region->last_frame() - pos;
2229 _editor->begin_reversible_command (_("change fade out length"));
2231 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2233 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2239 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2240 XMLNode &before = alist->get_state();
2242 tmp->audio_region()->set_fade_out_length (fade_length);
2243 tmp->audio_region()->set_fade_out_active (true);
2244 tmp->hide_fade_line();
2246 XMLNode &after = alist->get_state();
2247 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2250 _editor->commit_reversible_command ();
2254 FadeOutDrag::aborted ()
2256 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2257 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2263 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2264 tmp->hide_fade_line();
2268 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2271 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2273 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2276 _points.push_back (Gnome::Art::Point (0, 0));
2277 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2279 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2280 _line->property_width_pixels() = 1;
2281 _line->property_points () = _points;
2284 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2287 MarkerDrag::~MarkerDrag ()
2289 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2295 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2297 Drag::start_grab (event, cursor);
2301 Location *location = _editor->find_location_from_marker (_marker, is_start);
2302 _editor->_dragging_edit_point = true;
2304 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2306 update_item (location);
2308 // _drag_line->show();
2309 // _line->raise_to_top();
2312 _editor->show_verbose_time_cursor (location->start(), 10);
2314 _editor->show_verbose_time_cursor (location->end(), 10);
2317 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2320 case Selection::Toggle:
2321 _editor->selection->toggle (_marker);
2323 case Selection::Set:
2324 if (!_editor->selection->selected (_marker)) {
2325 _editor->selection->set (_marker);
2328 case Selection::Extend:
2330 Locations::LocationList ll;
2331 list<Marker*> to_add;
2333 _editor->selection->markers.range (s, e);
2334 s = min (_marker->position(), s);
2335 e = max (_marker->position(), e);
2338 if (e < max_framepos) {
2341 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2342 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2343 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2346 to_add.push_back (lm->start);
2349 to_add.push_back (lm->end);
2353 if (!to_add.empty()) {
2354 _editor->selection->add (to_add);
2358 case Selection::Add:
2359 _editor->selection->add (_marker);
2363 /* Set up copies for us to manipulate during the drag */
2365 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2366 Location* l = _editor->find_location_from_marker (*i, is_start);
2367 _copied_locations.push_back (new Location (*l));
2372 MarkerDrag::motion (GdkEvent* event, bool)
2374 framecnt_t f_delta = 0;
2376 bool move_both = false;
2378 Location *real_location;
2379 Location *copy_location = 0;
2381 framepos_t const newframe = adjusted_current_frame (event);
2383 framepos_t next = newframe;
2385 if (newframe == last_pointer_frame()) {
2389 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2393 MarkerSelection::iterator i;
2394 list<Location*>::iterator x;
2396 /* find the marker we're dragging, and compute the delta */
2398 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2399 x != _copied_locations.end() && i != _editor->selection->markers.end();
2405 if (marker == _marker) {
2407 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2412 if (real_location->is_mark()) {
2413 f_delta = newframe - copy_location->start();
2417 switch (marker->type()) {
2419 case Marker::LoopStart:
2420 case Marker::PunchIn:
2421 f_delta = newframe - copy_location->start();
2425 case Marker::LoopEnd:
2426 case Marker::PunchOut:
2427 f_delta = newframe - copy_location->end();
2430 /* what kind of marker is this ? */
2438 if (i == _editor->selection->markers.end()) {
2439 /* hmm, impossible - we didn't find the dragged marker */
2443 /* now move them all */
2445 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2446 x != _copied_locations.end() && i != _editor->selection->markers.end();
2452 /* call this to find out if its the start or end */
2454 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2458 if (real_location->locked()) {
2462 if (copy_location->is_mark()) {
2466 copy_location->set_start (copy_location->start() + f_delta);
2470 framepos_t new_start = copy_location->start() + f_delta;
2471 framepos_t new_end = copy_location->end() + f_delta;
2473 if (is_start) { // start-of-range marker
2476 copy_location->set_start (new_start);
2477 copy_location->set_end (new_end);
2478 } else if (new_start < copy_location->end()) {
2479 copy_location->set_start (new_start);
2481 _editor->snap_to (next, 1, true);
2482 copy_location->set_end (next);
2483 copy_location->set_start (newframe);
2486 } else { // end marker
2489 copy_location->set_end (new_end);
2490 copy_location->set_start (new_start);
2491 } else if (new_end > copy_location->start()) {
2492 copy_location->set_end (new_end);
2493 } else if (newframe > 0) {
2494 _editor->snap_to (next, -1, true);
2495 copy_location->set_start (next);
2496 copy_location->set_end (newframe);
2501 update_item (copy_location);
2503 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2506 lm->set_position (copy_location->start(), copy_location->end());
2510 assert (!_copied_locations.empty());
2512 _editor->show_verbose_time_cursor (newframe, 10);
2515 _editor->update_canvas_now ();
2520 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2522 if (!movement_occurred) {
2524 /* just a click, do nothing but finish
2525 off the selection process
2528 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2531 case Selection::Set:
2532 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2533 _editor->selection->set (_marker);
2537 case Selection::Toggle:
2538 case Selection::Extend:
2539 case Selection::Add:
2546 _editor->_dragging_edit_point = false;
2548 _editor->begin_reversible_command ( _("move marker") );
2549 XMLNode &before = _editor->session()->locations()->get_state();
2551 MarkerSelection::iterator i;
2552 list<Location*>::iterator x;
2555 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2556 x != _copied_locations.end() && i != _editor->selection->markers.end();
2559 Location * location = _editor->find_location_from_marker (*i, is_start);
2563 if (location->locked()) {
2567 if (location->is_mark()) {
2568 location->set_start ((*x)->start());
2570 location->set ((*x)->start(), (*x)->end());
2575 XMLNode &after = _editor->session()->locations()->get_state();
2576 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2577 _editor->commit_reversible_command ();
2583 MarkerDrag::aborted ()
2589 MarkerDrag::update_item (Location* location)
2591 double const x1 = _editor->frame_to_pixel (location->start());
2593 _points.front().set_x(x1);
2594 _points.back().set_x(x1);
2595 _line->property_points() = _points;
2598 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2600 _cumulative_x_drag (0),
2601 _cumulative_y_drag (0)
2603 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2605 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2611 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2613 Drag::start_grab (event, _editor->fader_cursor);
2615 // start the grab at the center of the control point so
2616 // the point doesn't 'jump' to the mouse after the first drag
2617 _fixed_grab_x = _point->get_x();
2618 _fixed_grab_y = _point->get_y();
2620 float const fraction = 1 - (_point->get_y() / _point->line().height());
2622 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2624 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2625 event->button.x + 10, event->button.y + 10);
2627 _editor->show_verbose_canvas_cursor ();
2631 ControlPointDrag::motion (GdkEvent* event, bool)
2633 double dx = _drags->current_pointer_x() - last_pointer_x();
2634 double dy = _drags->current_pointer_y() - last_pointer_y();
2636 if (event->button.state & Keyboard::SecondaryModifier) {
2641 /* coordinate in pixels relative to the start of the region (for region-based automation)
2642 or track (for track-based automation) */
2643 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2644 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2646 // calculate zero crossing point. back off by .01 to stay on the
2647 // positive side of zero
2648 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2650 // make sure we hit zero when passing through
2651 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2655 if (_x_constrained) {
2658 if (_y_constrained) {
2662 _cumulative_x_drag = cx - _fixed_grab_x;
2663 _cumulative_y_drag = cy - _fixed_grab_y;
2667 cy = min ((double) _point->line().height(), cy);
2669 framepos_t cx_frames = _editor->unit_to_frame (cx);
2671 if (!_x_constrained) {
2672 _editor->snap_to_with_modifier (cx_frames, event);
2675 cx_frames = min (cx_frames, _point->line().maximum_time());
2677 float const fraction = 1.0 - (cy / _point->line().height());
2679 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2681 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2683 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2687 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2689 if (!movement_occurred) {
2693 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2694 _editor->reset_point_selection ();
2698 motion (event, false);
2701 _point->line().end_drag ();
2702 _editor->session()->commit_reversible_command ();
2706 ControlPointDrag::aborted ()
2708 _point->line().reset ();
2712 ControlPointDrag::active (Editing::MouseMode m)
2714 if (m == Editing::MouseGain) {
2715 /* always active in mouse gain */
2719 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2720 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2723 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2726 _cumulative_y_drag (0)
2728 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2732 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2734 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2737 _item = &_line->grab_item ();
2739 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2740 origin, and ditto for y.
2743 double cx = event->button.x;
2744 double cy = event->button.y;
2746 _line->parent_group().w2i (cx, cy);
2748 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2753 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2754 /* no adjacent points */
2758 Drag::start_grab (event, _editor->fader_cursor);
2760 /* store grab start in parent frame */
2765 double fraction = 1.0 - (cy / _line->height());
2767 _line->start_drag_line (before, after, fraction);
2769 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2770 event->button.x + 10, event->button.y + 10);
2772 _editor->show_verbose_canvas_cursor ();
2776 LineDrag::motion (GdkEvent* event, bool)
2778 double dy = _drags->current_pointer_y() - last_pointer_y();
2780 if (event->button.state & Keyboard::SecondaryModifier) {
2784 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2786 _cumulative_y_drag = cy - _fixed_grab_y;
2789 cy = min ((double) _line->height(), cy);
2791 double const fraction = 1.0 - (cy / _line->height());
2795 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2801 /* we are ignoring x position for this drag, so we can just pass in anything */
2802 _line->drag_motion (0, fraction, true, push);
2804 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2808 LineDrag::finished (GdkEvent* event, bool)
2810 motion (event, false);
2812 _editor->session()->commit_reversible_command ();
2816 LineDrag::aborted ()
2821 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2824 _cumulative_x_drag (0)
2826 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2830 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2832 Drag::start_grab (event);
2834 _line = reinterpret_cast<SimpleLine*> (_item);
2837 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2839 double cx = event->button.x;
2840 double cy = event->button.y;
2842 _item->property_parent().get_value()->w2i(cx, cy);
2844 /* store grab start in parent frame */
2845 _region_view_grab_x = cx;
2847 _before = _line->property_x1();
2849 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2851 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2855 FeatureLineDrag::motion (GdkEvent* event, bool)
2857 double dx = _drags->current_pointer_x() - last_pointer_x();
2859 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2861 _cumulative_x_drag += dx;
2863 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2872 _line->property_x1() = cx;
2873 _line->property_x2() = cx;
2875 _before = _line->property_x1();
2879 FeatureLineDrag::finished (GdkEvent* event, bool)
2881 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2882 _arv->update_transient(_before, _line->property_x1());
2886 FeatureLineDrag::aborted ()
2891 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2894 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2898 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2900 Drag::start_grab (event);
2901 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2905 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2912 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2914 framepos_t grab = grab_frame ();
2915 if (Config->get_rubberbanding_snaps_to_grid ()) {
2916 _editor->snap_to_with_modifier (grab, event);
2919 /* base start and end on initial click position */
2929 if (_drags->current_pointer_y() < grab_y()) {
2930 y1 = _drags->current_pointer_y();
2933 y2 = _drags->current_pointer_y();
2938 if (start != end || y1 != y2) {
2940 double x1 = _editor->frame_to_pixel (start);
2941 double x2 = _editor->frame_to_pixel (end);
2943 _editor->rubberband_rect->property_x1() = x1;
2944 _editor->rubberband_rect->property_y1() = y1;
2945 _editor->rubberband_rect->property_x2() = x2;
2946 _editor->rubberband_rect->property_y2() = y2;
2948 _editor->rubberband_rect->show();
2949 _editor->rubberband_rect->raise_to_top();
2951 _editor->show_verbose_time_cursor (pf, 10);
2956 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2958 if (movement_occurred) {
2960 motion (event, false);
2963 if (_drags->current_pointer_y() < grab_y()) {
2964 y1 = _drags->current_pointer_y();
2967 y2 = _drags->current_pointer_y();
2972 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2975 _editor->begin_reversible_command (_("rubberband selection"));
2977 if (grab_frame() < last_pointer_frame()) {
2978 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
2980 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
2984 _editor->commit_reversible_command ();
2988 if (!getenv("ARDOUR_SAE")) {
2989 _editor->selection->clear_tracks();
2991 _editor->selection->clear_regions();
2992 _editor->selection->clear_points ();
2993 _editor->selection->clear_lines ();
2996 _editor->rubberband_rect->hide();
3000 RubberbandSelectDrag::aborted ()
3002 _editor->rubberband_rect->hide ();
3005 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3006 : RegionDrag (e, i, p, v)
3008 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3012 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3014 Drag::start_grab (event, cursor);
3016 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3020 TimeFXDrag::motion (GdkEvent* event, bool)
3022 RegionView* rv = _primary;
3024 framepos_t const pf = adjusted_current_frame (event);
3026 if (pf > rv->region()->position()) {
3027 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3030 _editor->show_verbose_time_cursor (pf, 10);
3034 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3036 _primary->get_time_axis_view().hide_timestretch ();
3038 if (!movement_occurred) {
3042 if (last_pointer_frame() < _primary->region()->position()) {
3043 /* backwards drag of the left edge - not usable */
3047 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3049 float percentage = (double) newlen / (double) _primary->region()->length();
3051 #ifndef USE_RUBBERBAND
3052 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3053 if (_primary->region()->data_type() == DataType::AUDIO) {
3054 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3058 _editor->begin_reversible_command (_("timestretch"));
3060 // XXX how do timeFX on multiple regions ?
3065 if (_editor->time_stretch (rs, percentage) == -1) {
3066 error << _("An error occurred while executing time stretch operation") << endmsg;
3071 TimeFXDrag::aborted ()
3073 _primary->get_time_axis_view().hide_timestretch ();
3076 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3079 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3083 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3085 Drag::start_grab (event);
3089 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3091 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3095 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3097 if (movement_occurred && _editor->session()) {
3098 /* make sure we stop */
3099 _editor->session()->request_transport_speed (0.0);
3104 ScrubDrag::aborted ()
3109 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3113 , _original_pointer_time_axis (-1)
3114 , _last_pointer_time_axis (-1)
3116 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3120 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3122 framepos_t start = 0;
3125 if (_editor->session() == 0) {
3129 Gdk::Cursor* cursor = 0;
3131 switch (_operation) {
3132 case CreateSelection:
3133 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3138 cursor = _editor->selector_cursor;
3139 Drag::start_grab (event, cursor);
3142 case SelectionStartTrim:
3143 if (_editor->clicked_axisview) {
3144 _editor->clicked_axisview->order_selection_trims (_item, true);
3146 Drag::start_grab (event, _editor->left_side_trim_cursor);
3147 start = _editor->selection->time[_editor->clicked_selection].start;
3148 _pointer_frame_offset = raw_grab_frame() - start;
3151 case SelectionEndTrim:
3152 if (_editor->clicked_axisview) {
3153 _editor->clicked_axisview->order_selection_trims (_item, false);
3155 Drag::start_grab (event, _editor->right_side_trim_cursor);
3156 end = _editor->selection->time[_editor->clicked_selection].end;
3157 _pointer_frame_offset = raw_grab_frame() - end;
3161 start = _editor->selection->time[_editor->clicked_selection].start;
3162 Drag::start_grab (event, cursor);
3163 _pointer_frame_offset = raw_grab_frame() - start;
3167 if (_operation == SelectionMove) {
3168 _editor->show_verbose_time_cursor (start, 10);
3170 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3173 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3177 SelectionDrag::motion (GdkEvent* event, bool first_move)
3179 framepos_t start = 0;
3183 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3184 if (pending_time_axis.first == 0) {
3188 framepos_t const pending_position = adjusted_current_frame (event);
3190 /* only alter selection if things have changed */
3192 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3196 switch (_operation) {
3197 case CreateSelection:
3199 framepos_t grab = grab_frame ();
3202 _editor->snap_to (grab);
3205 if (pending_position < grab_frame()) {
3206 start = pending_position;
3209 end = pending_position;
3213 /* first drag: Either add to the selection
3214 or create a new selection
3220 /* adding to the selection */
3221 _editor->set_selected_track_as_side_effect (Selection::Add);
3222 //_editor->selection->add (_editor->clicked_axisview);
3223 _editor->clicked_selection = _editor->selection->add (start, end);
3228 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3229 //_editor->selection->set (_editor->clicked_axisview);
3230 _editor->set_selected_track_as_side_effect (Selection::Set);
3233 _editor->clicked_selection = _editor->selection->set (start, end);
3237 /* select the track that we're in */
3238 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3239 // _editor->set_selected_track_as_side_effect (Selection::Add);
3240 _editor->selection->add (pending_time_axis.first);
3241 _added_time_axes.push_back (pending_time_axis.first);
3244 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3245 tracks that we selected in the first place.
3248 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3249 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3251 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3252 while (i != _added_time_axes.end()) {
3254 list<TimeAxisView*>::iterator tmp = i;
3257 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3258 _editor->selection->remove (*i);
3259 _added_time_axes.remove (*i);
3268 case SelectionStartTrim:
3270 start = _editor->selection->time[_editor->clicked_selection].start;
3271 end = _editor->selection->time[_editor->clicked_selection].end;
3273 if (pending_position > end) {
3276 start = pending_position;
3280 case SelectionEndTrim:
3282 start = _editor->selection->time[_editor->clicked_selection].start;
3283 end = _editor->selection->time[_editor->clicked_selection].end;
3285 if (pending_position < start) {
3288 end = pending_position;
3295 start = _editor->selection->time[_editor->clicked_selection].start;
3296 end = _editor->selection->time[_editor->clicked_selection].end;
3298 length = end - start;
3300 start = pending_position;
3301 _editor->snap_to (start);
3303 end = start + length;
3308 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3309 _editor->start_canvas_autoscroll (1, 0);
3313 _editor->selection->replace (_editor->clicked_selection, start, end);
3316 if (_operation == SelectionMove) {
3317 _editor->show_verbose_time_cursor(start, 10);
3319 _editor->show_verbose_time_cursor(pending_position, 10);
3324 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3326 Session* s = _editor->session();
3328 if (movement_occurred) {
3329 motion (event, false);
3330 /* XXX this is not object-oriented programming at all. ick */
3331 if (_editor->selection->time.consolidate()) {
3332 _editor->selection->TimeChanged ();
3335 /* XXX what if its a music time selection? */
3336 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3337 s->request_play_range (&_editor->selection->time, true);
3342 /* just a click, no pointer movement.*/
3344 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3345 _editor->selection->clear_time();
3348 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3349 _editor->selection->set (_editor->clicked_axisview);
3352 if (s && s->get_play_range () && s->transport_rolling()) {
3353 s->request_stop (false, false);
3358 _editor->stop_canvas_autoscroll ();
3362 SelectionDrag::aborted ()
3367 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3372 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3374 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3375 physical_screen_height (_editor->get_window()));
3376 _drag_rect->hide ();
3378 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3379 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3383 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3385 if (_editor->session() == 0) {
3389 Gdk::Cursor* cursor = 0;
3391 if (!_editor->temp_location) {
3392 _editor->temp_location = new Location (*_editor->session());
3395 switch (_operation) {
3396 case CreateRangeMarker:
3397 case CreateTransportMarker:
3398 case CreateCDMarker:
3400 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3405 cursor = _editor->selector_cursor;
3409 Drag::start_grab (event, cursor);
3411 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3415 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3417 framepos_t start = 0;
3419 ArdourCanvas::SimpleRect *crect;
3421 switch (_operation) {
3422 case CreateRangeMarker:
3423 crect = _editor->range_bar_drag_rect;
3425 case CreateTransportMarker:
3426 crect = _editor->transport_bar_drag_rect;
3428 case CreateCDMarker:
3429 crect = _editor->cd_marker_bar_drag_rect;
3432 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3437 framepos_t const pf = adjusted_current_frame (event);
3439 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3440 framepos_t grab = grab_frame ();
3441 _editor->snap_to (grab);
3443 if (pf < grab_frame()) {
3451 /* first drag: Either add to the selection
3452 or create a new selection.
3457 _editor->temp_location->set (start, end);
3461 update_item (_editor->temp_location);
3463 //_drag_rect->raise_to_top();
3468 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3469 _editor->start_canvas_autoscroll (1, 0);
3473 _editor->temp_location->set (start, end);
3475 double x1 = _editor->frame_to_pixel (start);
3476 double x2 = _editor->frame_to_pixel (end);
3477 crect->property_x1() = x1;
3478 crect->property_x2() = x2;
3480 update_item (_editor->temp_location);
3483 _editor->show_verbose_time_cursor (pf, 10);
3488 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3490 Location * newloc = 0;
3494 if (movement_occurred) {
3495 motion (event, false);
3498 switch (_operation) {
3499 case CreateRangeMarker:
3500 case CreateCDMarker:
3502 _editor->begin_reversible_command (_("new range marker"));
3503 XMLNode &before = _editor->session()->locations()->get_state();
3504 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3505 if (_operation == CreateCDMarker) {
3506 flags = Location::IsRangeMarker | Location::IsCDMarker;
3507 _editor->cd_marker_bar_drag_rect->hide();
3510 flags = Location::IsRangeMarker;
3511 _editor->range_bar_drag_rect->hide();
3513 newloc = new Location (
3514 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3517 _editor->session()->locations()->add (newloc, true);
3518 XMLNode &after = _editor->session()->locations()->get_state();
3519 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3520 _editor->commit_reversible_command ();
3524 case CreateTransportMarker:
3525 // popup menu to pick loop or punch
3526 _editor->new_transport_marker_context_menu (&event->button, _item);
3530 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3532 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3537 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3539 if (end == max_framepos) {
3540 end = _editor->session()->current_end_frame ();
3543 if (start == max_framepos) {
3544 start = _editor->session()->current_start_frame ();
3547 switch (_editor->mouse_mode) {
3549 /* find the two markers on either side and then make the selection from it */
3550 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3554 /* find the two markers on either side of the click and make the range out of it */
3555 _editor->selection->set (start, end);
3564 _editor->stop_canvas_autoscroll ();
3568 RangeMarkerBarDrag::aborted ()
3574 RangeMarkerBarDrag::update_item (Location* location)
3576 double const x1 = _editor->frame_to_pixel (location->start());
3577 double const x2 = _editor->frame_to_pixel (location->end());
3579 _drag_rect->property_x1() = x1;
3580 _drag_rect->property_x2() = x2;
3583 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3587 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3591 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3593 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3594 Drag::start_grab (event, _editor->zoom_out_cursor);
3597 Drag::start_grab (event, _editor->zoom_in_cursor);
3601 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3605 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3610 framepos_t const pf = adjusted_current_frame (event);
3612 framepos_t grab = grab_frame ();
3613 _editor->snap_to_with_modifier (grab, event);
3615 /* base start and end on initial click position */
3627 _editor->zoom_rect->show();
3628 _editor->zoom_rect->raise_to_top();
3631 _editor->reposition_zoom_rect(start, end);
3633 _editor->show_verbose_time_cursor (pf, 10);
3638 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3640 if (movement_occurred) {
3641 motion (event, false);
3643 if (grab_frame() < last_pointer_frame()) {
3644 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3646 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3649 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3652 _editor->zoom_rect->hide();
3656 MouseZoomDrag::aborted ()
3658 _editor->zoom_rect->hide ();
3661 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3663 , _cumulative_dx (0)
3664 , _cumulative_dy (0)
3666 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3668 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3669 _region = &_primary->region_view ();
3670 _note_height = _region->midi_stream_view()->note_height ();
3674 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3676 Drag::start_grab (event);
3678 if (!(_was_selected = _primary->selected())) {
3680 /* tertiary-click means extend selection - we'll do that on button release,
3681 so don't add it here, because otherwise we make it hard to figure
3682 out the "extend-to" range.
3685 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3688 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3691 _region->note_selected (_primary, true);
3693 _region->unique_select (_primary);
3699 /** @return Current total drag x change in frames */
3701 NoteDrag::total_dx () const
3704 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3706 /* primary note time */
3707 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3709 /* new time of the primary note relative to the region position */
3710 frameoffset_t const st = n + dx;
3712 /* snap and return corresponding delta */
3713 return _region->snap_frame_to_frame (st) - n;
3716 /** @return Current total drag y change in notes */
3718 NoteDrag::total_dy () const
3720 /* this is `backwards' to make increasing note number go in the right direction */
3721 double const dy = _drags->current_pointer_y() - grab_y();
3726 if (abs (dy) >= _note_height) {
3728 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3730 ndy = (int8_t) floor (dy / _note_height / 2.0);
3734 /* more positive value = higher pitch and higher y-axis position on track,
3735 which is the inverse of the X-centric geometric universe
3742 NoteDrag::motion (GdkEvent *, bool)
3744 /* Total change in x and y since the start of the drag */
3745 frameoffset_t const dx = total_dx ();
3746 int8_t const dy = -total_dy ();
3748 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3749 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3750 double const tdy = dy * _note_height - _cumulative_dy;
3753 _cumulative_dx += tdx;
3754 _cumulative_dy += tdy;
3756 int8_t note_delta = total_dy();
3758 _region->move_selection (tdx, tdy, note_delta);
3761 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3762 (int) floor (_primary->note()->note() + note_delta));
3764 _editor->show_verbose_canvas_cursor_with (buf);
3769 NoteDrag::finished (GdkEvent* ev, bool moved)
3772 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3774 if (_was_selected) {
3775 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3777 _region->note_deselected (_primary);
3780 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3781 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3783 if (!extend && !add && _region->selection_size() > 1) {
3784 _region->unique_select (_primary);
3785 } else if (extend) {
3786 _region->note_selected (_primary, true, true);
3788 /* it was added during button press */
3793 _region->note_dropped (_primary, total_dx(), total_dy());
3798 NoteDrag::aborted ()
3803 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3804 : Drag (editor, item)
3806 , _nothing_to_drag (false)
3808 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3810 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3813 /* get all lines in the automation view */
3814 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3816 /* find those that overlap the ranges being dragged */
3817 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3818 while (i != lines.end ()) {
3819 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3822 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3824 /* check this range against all the AudioRanges that we are using */
3825 list<AudioRange>::const_iterator k = _ranges.begin ();
3826 while (k != _ranges.end()) {
3827 if (k->coverage (r.first, r.second) != OverlapNone) {
3833 /* add it to our list if it overlaps at all */
3834 if (k != _ranges.end()) {
3839 _lines.push_back (n);
3845 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3849 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3851 Drag::start_grab (event, cursor);
3853 /* Get line states before we start changing things */
3854 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3855 i->state = &i->line->get_state ();
3858 if (_ranges.empty()) {
3860 /* No selected time ranges: drag all points */
3861 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3862 uint32_t const N = i->line->npoints ();
3863 for (uint32_t j = 0; j < N; ++j) {
3864 i->points.push_back (i->line->nth (j));
3870 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3872 framecnt_t const half = (i->start + i->end) / 2;
3874 /* find the line that this audio range starts in */
3875 list<Line>::iterator j = _lines.begin();
3876 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3880 if (j != _lines.end()) {
3881 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3883 /* j is the line that this audio range starts in; fade into it;
3884 64 samples length plucked out of thin air.
3887 framepos_t a = i->start + 64;
3892 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3893 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3895 the_list->add (p, the_list->eval (p));
3896 j->line->add_always_in_view (p);
3897 the_list->add (q, the_list->eval (q));
3898 j->line->add_always_in_view (q);
3901 /* same thing for the end */
3904 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3908 if (j != _lines.end()) {
3909 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3911 /* j is the line that this audio range starts in; fade out of it;
3912 64 samples length plucked out of thin air.
3915 framepos_t b = i->end - 64;
3920 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3921 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
3923 the_list->add (p, the_list->eval (p));
3924 j->line->add_always_in_view (p);
3925 the_list->add (q, the_list->eval (q));
3926 j->line->add_always_in_view (q);
3930 _nothing_to_drag = true;
3932 /* Find all the points that should be dragged and put them in the relevant
3933 points lists in the Line structs.
3936 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3938 uint32_t const N = i->line->npoints ();
3939 for (uint32_t j = 0; j < N; ++j) {
3941 /* here's a control point on this line */
3942 ControlPoint* p = i->line->nth (j);
3943 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
3945 /* see if it's inside a range */
3946 list<AudioRange>::const_iterator k = _ranges.begin ();
3947 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
3951 if (k != _ranges.end()) {
3952 /* dragging this point */
3953 _nothing_to_drag = false;
3954 i->points.push_back (p);
3960 if (_nothing_to_drag) {
3964 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3965 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
3970 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3972 if (_nothing_to_drag) {
3976 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3977 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
3979 /* we are ignoring x position for this drag, so we can just pass in anything */
3980 i->line->drag_motion (0, f, true, false);
3985 AutomationRangeDrag::finished (GdkEvent* event, bool)
3987 if (_nothing_to_drag) {
3991 motion (event, false);
3992 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3993 i->line->end_drag ();
3994 i->line->clear_always_in_view ();
3997 _editor->session()->commit_reversible_command ();
4001 AutomationRangeDrag::aborted ()
4003 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4004 i->line->clear_always_in_view ();
4009 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4012 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4013 layer = v->region()->layer ();
4014 initial_y = v->get_canvas_group()->property_y ();
4015 initial_playlist = v->region()->playlist ();