2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
26 #include "pbd/memento_command.h"
27 #include "pbd/basename.h"
28 #include "pbd/stateful_diff_command.h"
30 #include "gtkmm2ext/utils.h"
32 #include "ardour/session.h"
33 #include "ardour/dB.h"
34 #include "ardour/region_factory.h"
38 #include "audio_region_view.h"
39 #include "midi_region_view.h"
40 #include "ardour_ui.h"
41 #include "gui_thread.h"
42 #include "control_point.h"
44 #include "region_gain_line.h"
45 #include "editor_drag.h"
46 #include "audio_time_axis.h"
47 #include "midi_time_axis.h"
48 #include "canvas-note.h"
49 #include "selection.h"
50 #include "midi_selection.h"
51 #include "automation_time_axis.h"
53 #include "editor_cursors.h"
54 #include "mouse_cursors.h"
57 using namespace ARDOUR;
60 using namespace Gtkmm2ext;
61 using namespace Editing;
62 using namespace ArdourCanvas;
64 using Gtkmm2ext::Keyboard;
66 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
68 DragManager::DragManager (Editor* e)
71 , _current_pointer_frame (0)
76 DragManager::~DragManager ()
81 /** Call abort for each active drag */
87 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
98 DragManager::add (Drag* d)
100 d->set_manager (this);
101 _drags.push_back (d);
105 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
107 d->set_manager (this);
108 _drags.push_back (d);
113 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
115 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
117 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
118 (*i)->start_grab (e, c);
122 /** Call end_grab for each active drag.
123 * @return true if any drag reported movement having occurred.
126 DragManager::end_grab (GdkEvent* e)
131 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
132 bool const t = (*i)->end_grab (e);
147 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
151 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
153 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
154 bool const t = (*i)->motion_handler (e, from_autoscroll);
165 DragManager::have_item (ArdourCanvas::Item* i) const
167 list<Drag*>::const_iterator j = _drags.begin ();
168 while (j != _drags.end() && (*j)->item () != i) {
172 return j != _drags.end ();
175 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
178 , _pointer_frame_offset (0)
179 , _move_threshold_passed (false)
180 , _raw_grab_frame (0)
182 , _last_pointer_frame (0)
188 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
194 cursor = _editor->which_grabber_cursor ();
197 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
201 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
204 cursor = _editor->which_grabber_cursor ();
207 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
209 if (Keyboard::is_button2_event (&event->button)) {
210 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
211 _y_constrained = true;
212 _x_constrained = false;
214 _y_constrained = false;
215 _x_constrained = true;
218 _x_constrained = false;
219 _y_constrained = false;
222 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
223 setup_pointer_frame_offset ();
224 _grab_frame = adjusted_frame (_raw_grab_frame, event);
225 _last_pointer_frame = _grab_frame;
226 _last_pointer_x = _grab_x;
227 _last_pointer_y = _grab_y;
229 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
233 if (_editor->session() && _editor->session()->transport_rolling()) {
236 _was_rolling = false;
239 switch (_editor->snap_type()) {
240 case SnapToRegionStart:
241 case SnapToRegionEnd:
242 case SnapToRegionSync:
243 case SnapToRegionBoundary:
244 _editor->build_region_boundary_cache ();
251 /** Call to end a drag `successfully'. Ungrabs item and calls
252 * subclass' finished() method.
254 * @param event GDK event, or 0.
255 * @return true if some movement occurred, otherwise false.
258 Drag::end_grab (GdkEvent* event)
260 _editor->stop_canvas_autoscroll ();
262 _item->ungrab (event ? event->button.time : 0);
264 finished (event, _move_threshold_passed);
266 _editor->hide_verbose_canvas_cursor();
268 return _move_threshold_passed;
272 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
276 if (f > _pointer_frame_offset) {
277 pos = f - _pointer_frame_offset;
281 _editor->snap_to_with_modifier (pos, event);
288 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
290 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
294 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
296 /* check to see if we have moved in any way that matters since the last motion event */
297 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
298 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
302 pair<framecnt_t, int> const threshold = move_threshold ();
304 bool const old_move_threshold_passed = _move_threshold_passed;
306 if (!from_autoscroll && !_move_threshold_passed) {
308 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
309 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
311 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
314 if (active (_editor->mouse_mode) && _move_threshold_passed) {
316 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
317 if (!from_autoscroll) {
318 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
321 motion (event, _move_threshold_passed != old_move_threshold_passed);
323 _last_pointer_x = _drags->current_pointer_x ();
324 _last_pointer_y = _drags->current_pointer_y ();
325 _last_pointer_frame = adjusted_current_frame (event);
333 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
343 _editor->stop_canvas_autoscroll ();
344 _editor->hide_verbose_canvas_cursor ();
347 struct EditorOrderTimeAxisViewSorter {
348 bool operator() (TimeAxisView* a, TimeAxisView* b) {
349 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
350 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
352 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
356 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
360 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
362 /* Make a list of non-hidden tracks to refer to during the drag */
364 TrackViewList track_views = _editor->track_views;
365 track_views.sort (EditorOrderTimeAxisViewSorter ());
367 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
368 if (!(*i)->hidden()) {
370 _time_axis_views.push_back (*i);
372 TimeAxisView::Children children_list = (*i)->get_child_list ();
373 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
374 _time_axis_views.push_back (j->get());
379 /* the list of views can be empty at this point if this is a region list-insert drag
382 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
383 _views.push_back (DraggingView (*i, this));
386 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
390 RegionDrag::region_going_away (RegionView* v)
392 list<DraggingView>::iterator i = _views.begin ();
393 while (i != _views.end() && i->view != v) {
397 if (i != _views.end()) {
402 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
404 RegionDrag::find_time_axis_view (TimeAxisView* t) const
407 int const N = _time_axis_views.size ();
408 while (i < N && _time_axis_views[i] != t) {
419 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
420 : RegionDrag (e, i, p, v),
429 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
431 Drag::start_grab (event, cursor);
433 _editor->show_verbose_time_cursor (_last_frame_position, 10);
435 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
436 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
437 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
441 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
443 /* compute the amount of pointer motion in frames, and where
444 the region would be if we moved it by that much.
446 *pending_region_position = adjusted_current_frame (event);
448 framepos_t sync_frame;
449 framecnt_t sync_offset;
452 sync_offset = _primary->region()->sync_offset (sync_dir);
454 /* we don't handle a sync point that lies before zero.
456 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
458 sync_frame = *pending_region_position + (sync_dir*sync_offset);
460 _editor->snap_to_with_modifier (sync_frame, event);
462 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
465 *pending_region_position = _last_frame_position;
468 if (*pending_region_position > max_framepos - _primary->region()->length()) {
469 *pending_region_position = _last_frame_position;
474 /* in locked edit mode, reverse the usual meaning of _x_constrained */
475 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
477 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
479 /* x movement since last time */
480 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
482 /* total x movement */
483 framecnt_t total_dx = *pending_region_position;
484 if (regions_came_from_canvas()) {
485 total_dx = total_dx - grab_frame () + _pointer_frame_offset;
488 /* check that no regions have gone off the start of the session */
489 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
490 if ((i->view->region()->position() + total_dx) < 0) {
492 *pending_region_position = _last_frame_position;
497 _last_frame_position = *pending_region_position;
504 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
506 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
507 int const n = i->time_axis_view + delta_track;
508 if (n < 0 || n >= int (_time_axis_views.size())) {
509 /* off the top or bottom track */
513 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
514 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
515 /* not a track, or the wrong type */
519 int const l = i->layer + delta_layer;
520 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
521 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
522 If it has, the layers will be munged later anyway, so it's ok.
528 /* all regions being dragged are ok with this change */
533 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
535 assert (!_views.empty ());
537 /* Find the TimeAxisView that the pointer is now over */
538 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
540 /* Bail early if we're not over a track */
541 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
542 if (!rtv || !rtv->is_track()) {
543 _editor->hide_verbose_canvas_cursor ();
547 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
549 /* Here's the current pointer position in terms of time axis view and layer */
550 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
551 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
553 /* Work out the change in x */
554 framepos_t pending_region_position;
555 double const x_delta = compute_x_delta (event, &pending_region_position);
557 /* Work out the change in y */
558 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
559 int delta_layer = current_pointer_layer - _last_pointer_layer;
561 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
562 /* this y movement is not allowed, so do no y movement this time */
563 delta_time_axis_view = 0;
567 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
568 /* haven't reached next snap point, and we're not switching
569 trackviews nor layers. nothing to do.
574 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
576 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
578 RegionView* rv = i->view;
580 if (rv->region()->locked()) {
586 /* here we are calculating the y distance from the
587 top of the first track view to the top of the region
588 area of the track view that we're working on */
590 /* this x value is just a dummy value so that we have something
595 /* distance from the top of this track view to the region area
596 of our track view is always 1 */
600 /* convert to world coordinates, ie distance from the top of
603 rv->get_canvas_frame()->i2w (ix1, iy1);
605 /* compensate for the ruler section and the vertical scrollbar position */
606 iy1 += _editor->get_trackview_group_vertical_offset ();
608 // hide any dependent views
610 rv->get_time_axis_view().hide_dependent_views (*rv);
613 reparent to a non scrolling group so that we can keep the
614 region selection above all time axis views.
615 reparenting means we have to move the rv as the two
616 parent groups have different coordinates.
619 rv->get_canvas_group()->property_y() = iy1 - 1;
620 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
622 rv->fake_set_opaque (true);
625 /* Work out the change in y position of this region view */
629 /* If we have moved tracks, we'll fudge the layer delta so that the
630 region gets moved back onto layer 0 on its new track; this avoids
631 confusion when dragging regions from non-zero layers onto different
634 int this_delta_layer = delta_layer;
635 if (delta_time_axis_view != 0) {
636 this_delta_layer = - i->layer;
639 /* Move this region to layer 0 on its old track */
640 StreamView* lv = _time_axis_views[i->time_axis_view]->view ();
641 if (lv->layer_display() == Stacked) {
642 y_delta -= (lv->layers() - i->layer - 1) * lv->child_height ();
645 /* Now move it to its right layer on the current track */
646 StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view ();
647 if (cv->layer_display() == Stacked) {
648 y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height ();
652 if (delta_time_axis_view > 0) {
653 for (int j = 0; j < delta_time_axis_view; ++j) {
654 y_delta += _time_axis_views[i->time_axis_view + j]->current_height ();
657 /* start by subtracting the height of the track above where we are now */
658 for (int j = 1; j <= -delta_time_axis_view; ++j) {
659 y_delta -= _time_axis_views[i->time_axis_view - j]->current_height ();
664 rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ());
666 /* Update the DraggingView */
667 i->time_axis_view += delta_time_axis_view;
668 i->layer += this_delta_layer;
671 _editor->mouse_brush_insert_region (rv, pending_region_position);
673 rv->move (x_delta, y_delta);
676 } /* foreach region */
678 _total_x_delta += x_delta;
681 _editor->cursor_group->raise_to_top();
684 if (x_delta != 0 && !_brushing) {
685 _editor->show_verbose_time_cursor (_last_frame_position, 10);
688 _last_pointer_time_axis_view += delta_time_axis_view;
689 _last_pointer_layer += delta_layer;
693 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
695 if (_copy && first_move) {
697 /* duplicate the regionview(s) and region(s) */
699 list<DraggingView> new_regionviews;
701 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
703 RegionView* rv = i->view;
704 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
705 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
707 const boost::shared_ptr<const Region> original = rv->region();
708 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
709 region_copy->set_position (original->position(), this);
713 boost::shared_ptr<AudioRegion> audioregion_copy
714 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
716 nrv = new AudioRegionView (*arv, audioregion_copy);
718 boost::shared_ptr<MidiRegion> midiregion_copy
719 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
720 nrv = new MidiRegionView (*mrv, midiregion_copy);
725 nrv->get_canvas_group()->show ();
726 new_regionviews.push_back (DraggingView (nrv, this));
728 /* swap _primary to the copy */
730 if (rv == _primary) {
734 /* ..and deselect the one we copied */
736 rv->set_selected (false);
739 if (!new_regionviews.empty()) {
741 /* reflect the fact that we are dragging the copies */
743 _views = new_regionviews;
745 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
748 sync the canvas to what we think is its current state
749 without it, the canvas seems to
750 "forget" to update properly after the upcoming reparent()
751 ..only if the mouse is in rapid motion at the time of the grab.
752 something to do with regionview creation taking so long?
754 _editor->update_canvas_now();
758 RegionMotionDrag::motion (event, first_move);
762 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
764 if (!movement_occurred) {
769 /* reverse this here so that we have the correct logic to finalize
773 if (Config->get_edit_mode() == Lock) {
774 _x_constrained = !_x_constrained;
777 assert (!_views.empty ());
779 bool const changed_position = (_last_frame_position != _primary->region()->position());
780 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
781 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
783 _editor->update_canvas_now ();
805 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
807 RegionSelection new_views;
808 PlaylistSet modified_playlists;
809 list<RegionView*> views_to_delete;
812 /* all changes were made during motion event handlers */
814 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
818 _editor->commit_reversible_command ();
822 if (_x_constrained) {
823 _editor->begin_reversible_command (_("fixed time region copy"));
825 _editor->begin_reversible_command (_("region copy"));
828 /* insert the regions into their new playlists */
829 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
831 if (i->view->region()->locked()) {
837 if (changed_position && !_x_constrained) {
838 where = i->view->region()->position() - drag_delta;
840 where = i->view->region()->position();
843 RegionView* new_view = insert_region_into_playlist (
844 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
851 new_views.push_back (new_view);
853 /* we don't need the copied RegionView any more */
854 views_to_delete.push_back (i->view);
857 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
858 because when views are deleted they are automagically removed from _views, which messes
861 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
865 /* If we've created new regions either by copying or moving
866 to a new track, we want to replace the old selection with the new ones
869 if (new_views.size() > 0) {
870 _editor->selection->set (new_views);
873 /* write commands for the accumulated diffs for all our modified playlists */
874 add_stateful_diff_commands_for_playlists (modified_playlists);
876 _editor->commit_reversible_command ();
880 RegionMoveDrag::finished_no_copy (
881 bool const changed_position,
882 bool const changed_tracks,
883 framecnt_t const drag_delta
886 RegionSelection new_views;
887 PlaylistSet modified_playlists;
888 PlaylistSet frozen_playlists;
891 /* all changes were made during motion event handlers */
892 _editor->commit_reversible_command ();
896 if (_x_constrained) {
897 _editor->begin_reversible_command (_("fixed time region drag"));
899 _editor->begin_reversible_command (_("region drag"));
902 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
904 RegionView* rv = i->view;
906 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
907 layer_t const dest_layer = i->layer;
909 if (rv->region()->locked()) {
916 if (changed_position && !_x_constrained) {
917 where = rv->region()->position() - drag_delta;
919 where = rv->region()->position();
922 if (changed_tracks) {
924 /* insert into new playlist */
926 RegionView* new_view = insert_region_into_playlist (
927 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
935 new_views.push_back (new_view);
937 /* remove from old playlist */
939 /* the region that used to be in the old playlist is not
940 moved to the new one - we use a copy of it. as a result,
941 any existing editor for the region should no longer be
944 rv->hide_region_editor();
945 rv->fake_set_opaque (false);
947 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
951 rv->region()->clear_changes ();
954 motion on the same track. plonk the previously reparented region
955 back to its original canvas group (its streamview).
956 No need to do anything for copies as they are fake regions which will be deleted.
959 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
960 rv->get_canvas_group()->property_y() = i->initial_y;
961 rv->get_time_axis_view().reveal_dependent_views (*rv);
963 /* just change the model */
965 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
967 if (dest_rtv->view()->layer_display() == Stacked) {
968 rv->region()->set_layer (dest_layer);
969 rv->region()->set_pending_explicit_relayer (true);
972 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
974 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
980 /* this movement may result in a crossfade being modified, so we need to get undo
981 data from the playlist as well as the region.
984 r = modified_playlists.insert (playlist);
986 playlist->clear_changes ();
989 rv->region()->set_position (where, (void*) this);
991 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
994 if (changed_tracks) {
996 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
997 was selected in all of them, then removing it from a playlist will have removed all
998 trace of it from _views (i.e. there were N regions selected, we removed 1,
999 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1000 corresponding regionview, and _views is now empty).
1002 This could have invalidated any and all iterators into _views.
1004 The heuristic we use here is: if the region selection is empty, break out of the loop
1005 here. if the region selection is not empty, then restart the loop because we know that
1006 we must have removed at least the region(view) we've just been working on as well as any
1007 that we processed on previous iterations.
1009 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1010 we can just iterate.
1014 if (_views.empty()) {
1025 /* If we've created new regions either by copying or moving
1026 to a new track, we want to replace the old selection with the new ones
1029 if (new_views.size() > 0) {
1030 _editor->selection->set (new_views);
1033 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1037 /* write commands for the accumulated diffs for all our modified playlists */
1038 add_stateful_diff_commands_for_playlists (modified_playlists);
1040 _editor->commit_reversible_command ();
1043 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1044 * @param region Region to remove.
1045 * @param playlist playlist To remove from.
1046 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1047 * that clear_changes () is only called once per playlist.
1050 RegionMoveDrag::remove_region_from_playlist (
1051 boost::shared_ptr<Region> region,
1052 boost::shared_ptr<Playlist> playlist,
1053 PlaylistSet& modified_playlists
1056 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1059 playlist->clear_changes ();
1062 playlist->remove_region (region);
1066 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1067 * clearing the playlist's diff history first if necessary.
1068 * @param region Region to insert.
1069 * @param dest_rtv Destination RouteTimeAxisView.
1070 * @param dest_layer Destination layer.
1071 * @param where Destination position.
1072 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1073 * that clear_changes () is only called once per playlist.
1074 * @return New RegionView, or 0 if no insert was performed.
1077 RegionMoveDrag::insert_region_into_playlist (
1078 boost::shared_ptr<Region> region,
1079 RouteTimeAxisView* dest_rtv,
1082 PlaylistSet& modified_playlists
1085 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1086 if (!dest_playlist) {
1090 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1091 _new_region_view = 0;
1092 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1094 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1095 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1097 dest_playlist->clear_changes ();
1100 dest_playlist->add_region (region, where);
1102 if (dest_rtv->view()->layer_display() == Stacked) {
1103 region->set_layer (dest_layer);
1104 region->set_pending_explicit_relayer (true);
1109 assert (_new_region_view);
1111 return _new_region_view;
1115 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1117 _new_region_view = rv;
1121 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1123 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1124 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1126 _editor->session()->add_command (new StatefulDiffCommand (*i));
1135 RegionMoveDrag::aborted ()
1139 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1146 RegionMotionDrag::aborted ();
1151 RegionMotionDrag::aborted ()
1153 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1154 RegionView* rv = i->view;
1155 TimeAxisView* tv = &(rv->get_time_axis_view ());
1156 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1158 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1159 rv->get_canvas_group()->property_y() = 0;
1160 rv->get_time_axis_view().reveal_dependent_views (*rv);
1161 rv->fake_set_opaque (false);
1162 rv->move (-_total_x_delta, 0);
1163 rv->set_height (rtv->view()->child_height ());
1166 _editor->update_canvas_now ();
1169 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1170 : RegionMotionDrag (e, i, p, v, b),
1173 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1176 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1177 if (rtv && rtv->is_track()) {
1178 speed = rtv->track()->speed ();
1181 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1185 RegionMoveDrag::setup_pointer_frame_offset ()
1187 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1190 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1191 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1193 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1195 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1196 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1198 _primary = v->view()->create_region_view (r, false, false);
1200 _primary->get_canvas_group()->show ();
1201 _primary->set_position (pos, 0);
1202 _views.push_back (DraggingView (_primary, this));
1204 _last_frame_position = pos;
1206 _item = _primary->get_canvas_group ();
1210 RegionInsertDrag::finished (GdkEvent *, bool)
1212 _editor->update_canvas_now ();
1214 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1216 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1217 _primary->get_canvas_group()->property_y() = 0;
1219 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1221 _editor->begin_reversible_command (_("insert region"));
1222 playlist->clear_changes ();
1223 playlist->add_region (_primary->region (), _last_frame_position);
1224 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1225 _editor->commit_reversible_command ();
1233 RegionInsertDrag::aborted ()
1240 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1241 : RegionMoveDrag (e, i, p, v, false, false)
1243 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1246 struct RegionSelectionByPosition {
1247 bool operator() (RegionView*a, RegionView* b) {
1248 return a->region()->position () < b->region()->position();
1253 RegionSpliceDrag::motion (GdkEvent* event, bool)
1255 /* Which trackview is this ? */
1257 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1258 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1259 layer_t layer = tvp.second;
1261 if (tv && tv->layer_display() == Overlaid) {
1265 /* The region motion is only processed if the pointer is over
1269 if (!tv || !tv->is_track()) {
1270 /* To make sure we hide the verbose canvas cursor when the mouse is
1271 not held over and audiotrack.
1273 _editor->hide_verbose_canvas_cursor ();
1279 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1285 RegionSelection copy (_editor->selection->regions);
1287 RegionSelectionByPosition cmp;
1290 framepos_t const pf = adjusted_current_frame (event);
1292 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1294 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1300 boost::shared_ptr<Playlist> playlist;
1302 if ((playlist = atv->playlist()) == 0) {
1306 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1311 if (pf < (*i)->region()->last_frame() + 1) {
1315 if (pf > (*i)->region()->first_frame()) {
1321 playlist->shuffle ((*i)->region(), dir);
1326 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1328 RegionMoveDrag::finished (event, movement_occurred);
1332 RegionSpliceDrag::aborted ()
1337 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1339 _view (dynamic_cast<MidiTimeAxisView*> (v))
1341 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1347 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1353 framepos_t const f = adjusted_current_frame (event);
1354 if (f < grab_frame()) {
1355 _region->set_position (f, this);
1358 /* again, don't use a zero-length region (see above) */
1359 framecnt_t const len = abs (f - grab_frame ());
1360 _region->set_length (len < 1 ? 1 : len, this);
1366 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1368 if (!movement_occurred) {
1373 _editor->commit_reversible_command ();
1378 RegionCreateDrag::add_region ()
1380 if (_editor->session()) {
1381 const TempoMap& map (_editor->session()->tempo_map());
1382 framecnt_t pos = grab_frame();
1383 const Meter& m = map.meter_at (pos);
1384 /* not that the frame rate used here can be affected by pull up/down which
1387 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1388 _region = _view->add_region (grab_frame(), len, false);
1393 RegionCreateDrag::aborted ()
1398 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1402 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1406 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1408 Gdk::Cursor* cursor;
1409 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1410 float x_fraction = cnote->mouse_x_fraction ();
1412 if (x_fraction > 0.0 && x_fraction < 0.25) {
1413 cursor = _editor->cursors()->left_side_trim;
1415 cursor = _editor->cursors()->right_side_trim;
1418 Drag::start_grab (event, cursor);
1420 region = &cnote->region_view();
1422 double const region_start = region->get_position_pixels();
1423 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1425 if (grab_x() <= middle_point) {
1426 cursor = _editor->cursors()->left_side_trim;
1429 cursor = _editor->cursors()->right_side_trim;
1433 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1435 if (event->motion.state & Keyboard::PrimaryModifier) {
1441 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1443 if (ms.size() > 1) {
1444 /* has to be relative, may make no sense otherwise */
1448 /* select this note; if it is already selected, preserve the existing selection,
1449 otherwise make this note the only one selected.
1451 region->note_selected (cnote, cnote->selected ());
1453 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1454 MidiRegionSelection::iterator next;
1457 (*r)->begin_resizing (at_front);
1463 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1465 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1466 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1467 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1472 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1474 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1475 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1476 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1481 NoteResizeDrag::aborted ()
1486 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1489 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1493 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1499 RegionGainDrag::finished (GdkEvent *, bool)
1505 RegionGainDrag::aborted ()
1510 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1511 : RegionDrag (e, i, p, v)
1512 , _have_transaction (false)
1514 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1518 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1521 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1522 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1524 if (tv && tv->is_track()) {
1525 speed = tv->track()->speed();
1528 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1529 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1530 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1532 framepos_t const pf = adjusted_current_frame (event);
1534 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1535 /* Move the contents of the region around without changing the region bounds */
1536 _operation = ContentsTrim;
1537 Drag::start_grab (event, _editor->cursors()->trimmer);
1539 /* These will get overridden for a point trim.*/
1540 if (pf < (region_start + region_length/2)) {
1541 /* closer to front */
1542 _operation = StartTrim;
1543 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1546 _operation = EndTrim;
1547 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1551 switch (_operation) {
1553 _editor->show_verbose_time_cursor (region_start, 10);
1554 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1555 i->view->trim_front_starting ();
1559 _editor->show_verbose_time_cursor (region_end, 10);
1562 _editor->show_verbose_time_cursor (pf, 10);
1566 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1567 i->view->region()->suspend_property_changes ();
1572 TrimDrag::motion (GdkEvent* event, bool first_move)
1574 RegionView* rv = _primary;
1577 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1578 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1579 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1581 if (tv && tv->is_track()) {
1582 speed = tv->track()->speed();
1585 framecnt_t const dt = adjusted_current_frame (event) - grab_frame ();
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 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1641 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1642 i->view->trim_end (i->initial_end + dt, 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() > adjusted_current_frame(event)) {
1658 left_direction = true;
1661 if (left_direction) {
1662 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1664 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1667 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1668 i->view->trim_contents (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 (adjusted_current_frame (event), 10);
1689 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1691 if (movement_occurred) {
1692 motion (event, false);
1694 /* This must happen before the region's StatefulDiffCommand is created, as it may
1695 `correct' (ahem) the region's _start from being negative to being zero. It
1696 needs to be zero in the undo record.
1698 if (_operation == StartTrim) {
1699 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1700 i->view->trim_front_ending ();
1704 if (!_editor->selection->selected (_primary)) {
1705 _primary->thaw_after_trim ();
1708 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1709 i->view->thaw_after_trim ();
1710 i->view->enable_display (true);
1711 i->view->fake_set_opaque (true);
1712 if (_have_transaction) {
1713 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1717 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1721 _editor->motion_frozen_playlists.clear ();
1722 if (_have_transaction) {
1723 _editor->commit_reversible_command();
1727 /* no mouse movement */
1728 _editor->point_trim (event, adjusted_current_frame (event));
1731 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1732 if (_operation == StartTrim) {
1733 i->view->trim_front_ending ();
1736 i->view->region()->resume_property_changes ();
1741 TrimDrag::aborted ()
1743 /* Our motion method is changing model state, so use the Undo system
1744 to cancel. Perhaps not ideal, as this will leave an Undo point
1745 behind which may be slightly odd from the user's point of view.
1750 if (_have_transaction) {
1754 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1755 i->view->region()->resume_property_changes ();
1759 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1763 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1765 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1770 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1773 // create a dummy marker for visual representation of moving the copy.
1774 // The actual copying is not done before we reach the finish callback.
1776 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1778 MeterMarker* new_marker = new MeterMarker (
1780 *_editor->meter_group,
1781 *_editor->cursor_group,
1782 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1784 *new MeterSection (_marker->meter())
1787 _item = &new_marker->the_item ();
1788 _marker = new_marker;
1792 MetricSection& section (_marker->meter());
1794 if (!section.movable()) {
1800 Drag::start_grab (event, cursor);
1802 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1806 MeterMarkerDrag::setup_pointer_frame_offset ()
1808 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1812 MeterMarkerDrag::motion (GdkEvent* event, bool)
1814 framepos_t const pf = adjusted_current_frame (event);
1816 _marker->set_position (pf);
1818 _editor->show_verbose_time_cursor (pf, 10);
1822 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1824 if (!movement_occurred) {
1828 motion (event, false);
1830 Timecode::BBT_Time when;
1832 TempoMap& map (_editor->session()->tempo_map());
1833 map.bbt_time (last_pointer_frame(), when);
1835 if (_copy == true) {
1836 _editor->begin_reversible_command (_("copy meter mark"));
1837 XMLNode &before = map.get_state();
1838 map.add_meter (_marker->meter(), when);
1839 XMLNode &after = map.get_state();
1840 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1841 _editor->commit_reversible_command ();
1843 // delete the dummy marker we used for visual representation of copying.
1844 // a new visual marker will show up automatically.
1847 _editor->begin_reversible_command (_("move meter mark"));
1848 XMLNode &before = map.get_state();
1849 map.move_meter (_marker->meter(), when);
1850 XMLNode &after = map.get_state();
1851 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1852 _editor->commit_reversible_command ();
1857 MeterMarkerDrag::aborted ()
1859 _marker->set_position (_marker->meter().frame ());
1862 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1866 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1868 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1873 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1877 // create a dummy marker for visual representation of moving the copy.
1878 // The actual copying is not done before we reach the finish callback.
1880 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1882 TempoMarker* new_marker = new TempoMarker (
1884 *_editor->tempo_group,
1885 *_editor->cursor_group,
1886 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1888 *new TempoSection (_marker->tempo())
1891 _item = &new_marker->the_item ();
1892 _marker = new_marker;
1896 Drag::start_grab (event, cursor);
1898 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1902 TempoMarkerDrag::setup_pointer_frame_offset ()
1904 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1908 TempoMarkerDrag::motion (GdkEvent* event, bool)
1910 framepos_t const pf = adjusted_current_frame (event);
1911 _marker->set_position (pf);
1912 _editor->show_verbose_time_cursor (pf, 10);
1916 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1918 if (!movement_occurred) {
1922 motion (event, false);
1924 Timecode::BBT_Time when;
1926 TempoMap& map (_editor->session()->tempo_map());
1927 map.bbt_time (last_pointer_frame(), when);
1929 if (_copy == true) {
1930 _editor->begin_reversible_command (_("copy tempo mark"));
1931 XMLNode &before = map.get_state();
1932 map.add_tempo (_marker->tempo(), when);
1933 XMLNode &after = map.get_state();
1934 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1935 _editor->commit_reversible_command ();
1937 // delete the dummy marker we used for visual representation of copying.
1938 // a new visual marker will show up automatically.
1941 _editor->begin_reversible_command (_("move tempo mark"));
1942 XMLNode &before = map.get_state();
1943 map.move_tempo (_marker->tempo(), when);
1944 XMLNode &after = map.get_state();
1945 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1946 _editor->commit_reversible_command ();
1951 TempoMarkerDrag::aborted ()
1953 _marker->set_position (_marker->tempo().frame());
1956 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1960 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
1962 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1967 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1969 Drag::start_grab (event, c);
1973 framepos_t where = _editor->event_frame (event, 0, 0);
1975 _editor->snap_to_with_modifier (where, event);
1976 _editor->playhead_cursor->set_position (where);
1980 if (_cursor == _editor->playhead_cursor) {
1981 _editor->_dragging_playhead = true;
1983 Session* s = _editor->session ();
1986 if (_was_rolling && _stop) {
1990 if (s->is_auditioning()) {
1991 s->cancel_audition ();
1994 s->request_suspend_timecode_transmission ();
1996 if (s->timecode_transmission_suspended ()) {
1997 framepos_t const f = _editor->playhead_cursor->current_frame;
1998 s->send_mmc_locate (f);
1999 s->send_full_time_code (f);
2004 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2008 CursorDrag::setup_pointer_frame_offset ()
2010 _pointer_frame_offset = raw_grab_frame() - _cursor->current_frame;
2014 CursorDrag::motion (GdkEvent* event, bool)
2016 framepos_t const adjusted_frame = adjusted_current_frame (event);
2018 if (adjusted_frame == last_pointer_frame()) {
2022 _cursor->set_position (adjusted_frame);
2024 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2026 Session* s = _editor->session ();
2027 if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
2028 framepos_t const f = _editor->playhead_cursor->current_frame;
2029 s->send_mmc_locate (f);
2030 s->send_full_time_code (f);
2035 _editor->update_canvas_now ();
2037 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2041 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2043 _editor->_dragging_playhead = false;
2045 if (!movement_occurred && _stop) {
2049 motion (event, false);
2051 if (_item == &_editor->playhead_cursor->canvas_item) {
2052 Session* s = _editor->session ();
2054 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2055 _editor->_pending_locate_request = true;
2056 s->request_resume_timecode_transmission ();
2062 CursorDrag::aborted ()
2064 if (_editor->_dragging_playhead) {
2065 _editor->session()->request_resume_timecode_transmission ();
2066 _editor->_dragging_playhead = false;
2069 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2072 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2073 : RegionDrag (e, i, p, v)
2075 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2079 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2081 Drag::start_grab (event, cursor);
2083 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2084 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2086 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2088 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2092 FadeInDrag::setup_pointer_frame_offset ()
2094 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2095 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2096 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2100 FadeInDrag::motion (GdkEvent* event, bool)
2102 framecnt_t fade_length;
2104 framepos_t const pos = adjusted_current_frame (event);
2106 boost::shared_ptr<Region> region = _primary->region ();
2108 if (pos < (region->position() + 64)) {
2109 fade_length = 64; // this should be a minimum defined somewhere
2110 } else if (pos > region->last_frame()) {
2111 fade_length = region->length();
2113 fade_length = pos - region->position();
2116 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2118 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2124 tmp->reset_fade_in_shape_width (fade_length);
2125 tmp->show_fade_line((framecnt_t) fade_length);
2128 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2132 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2134 if (!movement_occurred) {
2138 framecnt_t fade_length;
2140 framepos_t const pos = adjusted_current_frame (event);
2142 boost::shared_ptr<Region> region = _primary->region ();
2144 if (pos < (region->position() + 64)) {
2145 fade_length = 64; // this should be a minimum defined somewhere
2146 } else if (pos > region->last_frame()) {
2147 fade_length = region->length();
2149 fade_length = pos - region->position();
2152 _editor->begin_reversible_command (_("change fade in length"));
2154 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2156 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2162 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2163 XMLNode &before = alist->get_state();
2165 tmp->audio_region()->set_fade_in_length (fade_length);
2166 tmp->audio_region()->set_fade_in_active (true);
2167 tmp->hide_fade_line();
2169 XMLNode &after = alist->get_state();
2170 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2173 _editor->commit_reversible_command ();
2177 FadeInDrag::aborted ()
2179 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2180 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2186 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2187 tmp->hide_fade_line();
2191 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2192 : RegionDrag (e, i, p, v)
2194 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2198 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2200 Drag::start_grab (event, cursor);
2202 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2203 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2205 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2207 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2211 FadeOutDrag::setup_pointer_frame_offset ()
2213 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2214 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2215 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2219 FadeOutDrag::motion (GdkEvent* event, bool)
2221 framecnt_t fade_length;
2223 framepos_t const pos = adjusted_current_frame (event);
2225 boost::shared_ptr<Region> region = _primary->region ();
2227 if (pos > (region->last_frame() - 64)) {
2228 fade_length = 64; // this should really be a minimum fade defined somewhere
2230 else if (pos < region->position()) {
2231 fade_length = region->length();
2234 fade_length = region->last_frame() - pos;
2237 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2239 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2245 tmp->reset_fade_out_shape_width (fade_length);
2246 tmp->show_fade_line(region->length() - fade_length);
2249 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2253 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2255 if (!movement_occurred) {
2259 framecnt_t fade_length;
2261 framepos_t const pos = adjusted_current_frame (event);
2263 boost::shared_ptr<Region> region = _primary->region ();
2265 if (pos > (region->last_frame() - 64)) {
2266 fade_length = 64; // this should really be a minimum fade defined somewhere
2268 else if (pos < region->position()) {
2269 fade_length = region->length();
2272 fade_length = region->last_frame() - pos;
2275 _editor->begin_reversible_command (_("change fade out length"));
2277 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2279 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2285 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2286 XMLNode &before = alist->get_state();
2288 tmp->audio_region()->set_fade_out_length (fade_length);
2289 tmp->audio_region()->set_fade_out_active (true);
2290 tmp->hide_fade_line();
2292 XMLNode &after = alist->get_state();
2293 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2296 _editor->commit_reversible_command ();
2300 FadeOutDrag::aborted ()
2302 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2303 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2309 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2310 tmp->hide_fade_line();
2314 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2317 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2319 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2322 _points.push_back (Gnome::Art::Point (0, 0));
2323 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2325 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2326 _line->property_width_pixels() = 1;
2327 _line->property_points () = _points;
2330 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2333 MarkerDrag::~MarkerDrag ()
2335 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2341 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2343 Drag::start_grab (event, cursor);
2347 Location *location = _editor->find_location_from_marker (_marker, is_start);
2348 _editor->_dragging_edit_point = true;
2350 update_item (location);
2352 // _drag_line->show();
2353 // _line->raise_to_top();
2356 _editor->show_verbose_time_cursor (location->start(), 10);
2358 _editor->show_verbose_time_cursor (location->end(), 10);
2361 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2364 case Selection::Toggle:
2365 _editor->selection->toggle (_marker);
2367 case Selection::Set:
2368 if (!_editor->selection->selected (_marker)) {
2369 _editor->selection->set (_marker);
2372 case Selection::Extend:
2374 Locations::LocationList ll;
2375 list<Marker*> to_add;
2377 _editor->selection->markers.range (s, e);
2378 s = min (_marker->position(), s);
2379 e = max (_marker->position(), e);
2382 if (e < max_framepos) {
2385 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2386 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2387 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2390 to_add.push_back (lm->start);
2393 to_add.push_back (lm->end);
2397 if (!to_add.empty()) {
2398 _editor->selection->add (to_add);
2402 case Selection::Add:
2403 _editor->selection->add (_marker);
2407 /* Set up copies for us to manipulate during the drag */
2409 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2410 Location* l = _editor->find_location_from_marker (*i, is_start);
2411 _copied_locations.push_back (new Location (*l));
2416 MarkerDrag::setup_pointer_frame_offset ()
2419 Location *location = _editor->find_location_from_marker (_marker, is_start);
2420 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2424 MarkerDrag::motion (GdkEvent* event, bool)
2426 framecnt_t f_delta = 0;
2428 bool move_both = false;
2430 Location *real_location;
2431 Location *copy_location = 0;
2433 framepos_t const newframe = adjusted_current_frame (event);
2435 framepos_t next = newframe;
2437 if (newframe == last_pointer_frame()) {
2441 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2445 MarkerSelection::iterator i;
2446 list<Location*>::iterator x;
2448 /* find the marker we're dragging, and compute the delta */
2450 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2451 x != _copied_locations.end() && i != _editor->selection->markers.end();
2457 if (marker == _marker) {
2459 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2464 if (real_location->is_mark()) {
2465 f_delta = newframe - copy_location->start();
2469 switch (marker->type()) {
2470 case Marker::SessionStart:
2471 case Marker::RangeStart:
2472 case Marker::LoopStart:
2473 case Marker::PunchIn:
2474 f_delta = newframe - copy_location->start();
2477 case Marker::SessionEnd:
2478 case Marker::RangeEnd:
2479 case Marker::LoopEnd:
2480 case Marker::PunchOut:
2481 f_delta = newframe - copy_location->end();
2484 /* what kind of marker is this ? */
2492 if (i == _editor->selection->markers.end()) {
2493 /* hmm, impossible - we didn't find the dragged marker */
2497 /* now move them all */
2499 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2500 x != _copied_locations.end() && i != _editor->selection->markers.end();
2506 /* call this to find out if its the start or end */
2508 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2512 if (real_location->locked()) {
2516 if (copy_location->is_mark()) {
2520 copy_location->set_start (copy_location->start() + f_delta);
2524 framepos_t new_start = copy_location->start() + f_delta;
2525 framepos_t new_end = copy_location->end() + f_delta;
2527 if (is_start) { // start-of-range marker
2530 copy_location->set_start (new_start);
2531 copy_location->set_end (new_end);
2532 } else if (new_start < copy_location->end()) {
2533 copy_location->set_start (new_start);
2535 _editor->snap_to (next, 1, true);
2536 copy_location->set_end (next);
2537 copy_location->set_start (newframe);
2540 } else { // end marker
2543 copy_location->set_end (new_end);
2544 copy_location->set_start (new_start);
2545 } else if (new_end > copy_location->start()) {
2546 copy_location->set_end (new_end);
2547 } else if (newframe > 0) {
2548 _editor->snap_to (next, -1, true);
2549 copy_location->set_start (next);
2550 copy_location->set_end (newframe);
2555 update_item (copy_location);
2557 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2560 lm->set_position (copy_location->start(), copy_location->end());
2564 assert (!_copied_locations.empty());
2566 _editor->show_verbose_time_cursor (newframe, 10);
2569 _editor->update_canvas_now ();
2574 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2576 if (!movement_occurred) {
2578 /* just a click, do nothing but finish
2579 off the selection process
2582 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2585 case Selection::Set:
2586 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2587 _editor->selection->set (_marker);
2591 case Selection::Toggle:
2592 case Selection::Extend:
2593 case Selection::Add:
2600 _editor->_dragging_edit_point = false;
2602 _editor->begin_reversible_command ( _("move marker") );
2603 XMLNode &before = _editor->session()->locations()->get_state();
2605 MarkerSelection::iterator i;
2606 list<Location*>::iterator x;
2609 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2610 x != _copied_locations.end() && i != _editor->selection->markers.end();
2613 Location * location = _editor->find_location_from_marker (*i, is_start);
2617 if (location->locked()) {
2621 if (location->is_mark()) {
2622 location->set_start ((*x)->start());
2624 location->set ((*x)->start(), (*x)->end());
2629 XMLNode &after = _editor->session()->locations()->get_state();
2630 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2631 _editor->commit_reversible_command ();
2637 MarkerDrag::aborted ()
2643 MarkerDrag::update_item (Location* location)
2645 double const x1 = _editor->frame_to_pixel (location->start());
2647 _points.front().set_x(x1);
2648 _points.back().set_x(x1);
2649 _line->property_points() = _points;
2652 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2654 _cumulative_x_drag (0),
2655 _cumulative_y_drag (0)
2657 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2659 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2665 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2667 Drag::start_grab (event, _editor->cursors()->fader);
2669 // start the grab at the center of the control point so
2670 // the point doesn't 'jump' to the mouse after the first drag
2671 _fixed_grab_x = _point->get_x();
2672 _fixed_grab_y = _point->get_y();
2674 float const fraction = 1 - (_point->get_y() / _point->line().height());
2676 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2678 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2679 event->button.x + 10, event->button.y + 10);
2681 _editor->show_verbose_canvas_cursor ();
2685 ControlPointDrag::motion (GdkEvent* event, bool)
2687 double dx = _drags->current_pointer_x() - last_pointer_x();
2688 double dy = _drags->current_pointer_y() - last_pointer_y();
2690 if (event->button.state & Keyboard::SecondaryModifier) {
2695 /* coordinate in pixels relative to the start of the region (for region-based automation)
2696 or track (for track-based automation) */
2697 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2698 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2700 // calculate zero crossing point. back off by .01 to stay on the
2701 // positive side of zero
2702 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2704 // make sure we hit zero when passing through
2705 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2709 if (_x_constrained) {
2712 if (_y_constrained) {
2716 _cumulative_x_drag = cx - _fixed_grab_x;
2717 _cumulative_y_drag = cy - _fixed_grab_y;
2721 cy = min ((double) _point->line().height(), cy);
2723 framepos_t cx_frames = _editor->unit_to_frame (cx);
2725 if (!_x_constrained) {
2726 _editor->snap_to_with_modifier (cx_frames, event);
2729 cx_frames = min (cx_frames, _point->line().maximum_time());
2731 float const fraction = 1.0 - (cy / _point->line().height());
2733 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2735 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2737 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2741 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2743 if (!movement_occurred) {
2747 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2748 _editor->reset_point_selection ();
2752 motion (event, false);
2755 _point->line().end_drag ();
2756 _editor->session()->commit_reversible_command ();
2760 ControlPointDrag::aborted ()
2762 _point->line().reset ();
2766 ControlPointDrag::active (Editing::MouseMode m)
2768 if (m == Editing::MouseGain) {
2769 /* always active in mouse gain */
2773 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2774 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2777 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2780 _cumulative_y_drag (0)
2782 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2786 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2788 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2791 _item = &_line->grab_item ();
2793 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2794 origin, and ditto for y.
2797 double cx = event->button.x;
2798 double cy = event->button.y;
2800 _line->parent_group().w2i (cx, cy);
2802 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2807 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2808 /* no adjacent points */
2812 Drag::start_grab (event, _editor->cursors()->fader);
2814 /* store grab start in parent frame */
2819 double fraction = 1.0 - (cy / _line->height());
2821 _line->start_drag_line (before, after, fraction);
2823 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2824 event->button.x + 10, event->button.y + 10);
2826 _editor->show_verbose_canvas_cursor ();
2830 LineDrag::motion (GdkEvent* event, bool)
2832 double dy = _drags->current_pointer_y() - last_pointer_y();
2834 if (event->button.state & Keyboard::SecondaryModifier) {
2838 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2840 _cumulative_y_drag = cy - _fixed_grab_y;
2843 cy = min ((double) _line->height(), cy);
2845 double const fraction = 1.0 - (cy / _line->height());
2849 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2855 /* we are ignoring x position for this drag, so we can just pass in anything */
2856 _line->drag_motion (0, fraction, true, push);
2858 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2862 LineDrag::finished (GdkEvent* event, bool)
2864 motion (event, false);
2866 _editor->session()->commit_reversible_command ();
2870 LineDrag::aborted ()
2875 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2878 _cumulative_x_drag (0)
2880 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2884 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2886 Drag::start_grab (event);
2888 _line = reinterpret_cast<SimpleLine*> (_item);
2891 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2893 double cx = event->button.x;
2894 double cy = event->button.y;
2896 _item->property_parent().get_value()->w2i(cx, cy);
2898 /* store grab start in parent frame */
2899 _region_view_grab_x = cx;
2901 _before = _line->property_x1();
2903 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2905 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2909 FeatureLineDrag::motion (GdkEvent*, bool)
2911 double dx = _drags->current_pointer_x() - last_pointer_x();
2913 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2915 _cumulative_x_drag += dx;
2917 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2926 _line->property_x1() = cx;
2927 _line->property_x2() = cx;
2929 _before = _line->property_x1();
2933 FeatureLineDrag::finished (GdkEvent*, bool)
2935 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2936 _arv->update_transient(_before, _line->property_x1());
2940 FeatureLineDrag::aborted ()
2945 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2948 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2952 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2954 Drag::start_grab (event);
2955 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2959 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2966 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2968 framepos_t grab = grab_frame ();
2969 if (Config->get_rubberbanding_snaps_to_grid ()) {
2970 _editor->snap_to_with_modifier (grab, event);
2973 /* base start and end on initial click position */
2983 if (_drags->current_pointer_y() < grab_y()) {
2984 y1 = _drags->current_pointer_y();
2987 y2 = _drags->current_pointer_y();
2992 if (start != end || y1 != y2) {
2994 double x1 = _editor->frame_to_pixel (start);
2995 double x2 = _editor->frame_to_pixel (end);
2997 _editor->rubberband_rect->property_x1() = x1;
2998 _editor->rubberband_rect->property_y1() = y1;
2999 _editor->rubberband_rect->property_x2() = x2;
3000 _editor->rubberband_rect->property_y2() = y2;
3002 _editor->rubberband_rect->show();
3003 _editor->rubberband_rect->raise_to_top();
3005 _editor->show_verbose_time_cursor (pf, 10);
3010 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3012 if (movement_occurred) {
3014 motion (event, false);
3017 if (_drags->current_pointer_y() < grab_y()) {
3018 y1 = _drags->current_pointer_y();
3021 y2 = _drags->current_pointer_y();
3026 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3029 _editor->begin_reversible_command (_("rubberband selection"));
3031 if (grab_frame() < last_pointer_frame()) {
3032 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3034 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3038 _editor->commit_reversible_command ();
3042 if (!getenv("ARDOUR_SAE")) {
3043 _editor->selection->clear_tracks();
3045 _editor->selection->clear_regions();
3046 _editor->selection->clear_points ();
3047 _editor->selection->clear_lines ();
3050 _editor->rubberband_rect->hide();
3054 RubberbandSelectDrag::aborted ()
3056 _editor->rubberband_rect->hide ();
3059 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3060 : RegionDrag (e, i, p, v)
3062 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3066 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3068 Drag::start_grab (event, cursor);
3070 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3074 TimeFXDrag::motion (GdkEvent* event, bool)
3076 RegionView* rv = _primary;
3078 framepos_t const pf = adjusted_current_frame (event);
3080 if (pf > rv->region()->position()) {
3081 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3084 _editor->show_verbose_time_cursor (pf, 10);
3088 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3090 _primary->get_time_axis_view().hide_timestretch ();
3092 if (!movement_occurred) {
3096 if (last_pointer_frame() < _primary->region()->position()) {
3097 /* backwards drag of the left edge - not usable */
3101 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3103 float percentage = (double) newlen / (double) _primary->region()->length();
3105 #ifndef USE_RUBBERBAND
3106 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3107 if (_primary->region()->data_type() == DataType::AUDIO) {
3108 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3112 _editor->begin_reversible_command (_("timestretch"));
3114 // XXX how do timeFX on multiple regions ?
3119 if (_editor->time_stretch (rs, percentage) == -1) {
3120 error << _("An error occurred while executing time stretch operation") << endmsg;
3125 TimeFXDrag::aborted ()
3127 _primary->get_time_axis_view().hide_timestretch ();
3130 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3133 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3137 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3139 Drag::start_grab (event);
3143 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3145 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3149 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3151 if (movement_occurred && _editor->session()) {
3152 /* make sure we stop */
3153 _editor->session()->request_transport_speed (0.0);
3158 ScrubDrag::aborted ()
3163 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3167 , _original_pointer_time_axis (-1)
3168 , _last_pointer_time_axis (-1)
3170 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3174 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3176 if (_editor->session() == 0) {
3180 Gdk::Cursor* cursor = 0;
3182 switch (_operation) {
3183 case CreateSelection:
3184 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3189 cursor = _editor->cursors()->selector;
3190 Drag::start_grab (event, cursor);
3193 case SelectionStartTrim:
3194 if (_editor->clicked_axisview) {
3195 _editor->clicked_axisview->order_selection_trims (_item, true);
3197 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3200 case SelectionEndTrim:
3201 if (_editor->clicked_axisview) {
3202 _editor->clicked_axisview->order_selection_trims (_item, false);
3204 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3208 Drag::start_grab (event, cursor);
3212 if (_operation == SelectionMove) {
3213 _editor->show_verbose_time_cursor (_editor->selection->time[_editor->clicked_selection].start, 10);
3215 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3218 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3222 SelectionDrag::setup_pointer_frame_offset ()
3224 switch (_operation) {
3225 case CreateSelection:
3226 _pointer_frame_offset = 0;
3229 case SelectionStartTrim:
3231 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3234 case SelectionEndTrim:
3235 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3241 SelectionDrag::motion (GdkEvent* event, bool first_move)
3243 framepos_t start = 0;
3247 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3248 if (pending_time_axis.first == 0) {
3252 framepos_t const pending_position = adjusted_current_frame (event);
3254 /* only alter selection if things have changed */
3256 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3260 switch (_operation) {
3261 case CreateSelection:
3263 framepos_t grab = grab_frame ();
3266 _editor->snap_to (grab);
3269 if (pending_position < grab_frame()) {
3270 start = pending_position;
3273 end = pending_position;
3277 /* first drag: Either add to the selection
3278 or create a new selection
3284 /* adding to the selection */
3285 _editor->set_selected_track_as_side_effect (Selection::Add);
3286 //_editor->selection->add (_editor->clicked_axisview);
3287 _editor->clicked_selection = _editor->selection->add (start, end);
3292 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3293 //_editor->selection->set (_editor->clicked_axisview);
3294 _editor->set_selected_track_as_side_effect (Selection::Set);
3297 _editor->clicked_selection = _editor->selection->set (start, end);
3301 /* select the track that we're in */
3302 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3303 // _editor->set_selected_track_as_side_effect (Selection::Add);
3304 _editor->selection->add (pending_time_axis.first);
3305 _added_time_axes.push_back (pending_time_axis.first);
3308 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3309 tracks that we selected in the first place.
3312 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3313 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3315 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3316 while (i != _added_time_axes.end()) {
3318 list<TimeAxisView*>::iterator tmp = i;
3321 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3322 _editor->selection->remove (*i);
3323 _added_time_axes.remove (*i);
3332 case SelectionStartTrim:
3334 start = _editor->selection->time[_editor->clicked_selection].start;
3335 end = _editor->selection->time[_editor->clicked_selection].end;
3337 if (pending_position > end) {
3340 start = pending_position;
3344 case SelectionEndTrim:
3346 start = _editor->selection->time[_editor->clicked_selection].start;
3347 end = _editor->selection->time[_editor->clicked_selection].end;
3349 if (pending_position < start) {
3352 end = pending_position;
3359 start = _editor->selection->time[_editor->clicked_selection].start;
3360 end = _editor->selection->time[_editor->clicked_selection].end;
3362 length = end - start;
3364 start = pending_position;
3365 _editor->snap_to (start);
3367 end = start + length;
3372 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3373 _editor->start_canvas_autoscroll (1, 0);
3377 _editor->selection->replace (_editor->clicked_selection, start, end);
3380 if (_operation == SelectionMove) {
3381 _editor->show_verbose_time_cursor(start, 10);
3383 _editor->show_verbose_time_cursor(pending_position, 10);
3388 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3390 Session* s = _editor->session();
3392 if (movement_occurred) {
3393 motion (event, false);
3394 /* XXX this is not object-oriented programming at all. ick */
3395 if (_editor->selection->time.consolidate()) {
3396 _editor->selection->TimeChanged ();
3399 /* XXX what if its a music time selection? */
3400 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3401 s->request_play_range (&_editor->selection->time, true);
3406 /* just a click, no pointer movement.*/
3408 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3409 _editor->selection->clear_time();
3412 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3413 _editor->selection->set (_editor->clicked_axisview);
3416 if (s && s->get_play_range () && s->transport_rolling()) {
3417 s->request_stop (false, false);
3422 _editor->stop_canvas_autoscroll ();
3426 SelectionDrag::aborted ()
3431 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3436 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3438 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3439 physical_screen_height (_editor->get_window()));
3440 _drag_rect->hide ();
3442 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3443 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3447 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3449 if (_editor->session() == 0) {
3453 Gdk::Cursor* cursor = 0;
3455 if (!_editor->temp_location) {
3456 _editor->temp_location = new Location (*_editor->session());
3459 switch (_operation) {
3460 case CreateRangeMarker:
3461 case CreateTransportMarker:
3462 case CreateCDMarker:
3464 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3469 cursor = _editor->cursors()->selector;
3473 Drag::start_grab (event, cursor);
3475 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3479 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3481 framepos_t start = 0;
3483 ArdourCanvas::SimpleRect *crect;
3485 switch (_operation) {
3486 case CreateRangeMarker:
3487 crect = _editor->range_bar_drag_rect;
3489 case CreateTransportMarker:
3490 crect = _editor->transport_bar_drag_rect;
3492 case CreateCDMarker:
3493 crect = _editor->cd_marker_bar_drag_rect;
3496 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3501 framepos_t const pf = adjusted_current_frame (event);
3503 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3504 framepos_t grab = grab_frame ();
3505 _editor->snap_to (grab);
3507 if (pf < grab_frame()) {
3515 /* first drag: Either add to the selection
3516 or create a new selection.
3521 _editor->temp_location->set (start, end);
3525 update_item (_editor->temp_location);
3527 //_drag_rect->raise_to_top();
3532 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3533 _editor->start_canvas_autoscroll (1, 0);
3537 _editor->temp_location->set (start, end);
3539 double x1 = _editor->frame_to_pixel (start);
3540 double x2 = _editor->frame_to_pixel (end);
3541 crect->property_x1() = x1;
3542 crect->property_x2() = x2;
3544 update_item (_editor->temp_location);
3547 _editor->show_verbose_time_cursor (pf, 10);
3552 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3554 Location * newloc = 0;
3558 if (movement_occurred) {
3559 motion (event, false);
3562 switch (_operation) {
3563 case CreateRangeMarker:
3564 case CreateCDMarker:
3566 _editor->begin_reversible_command (_("new range marker"));
3567 XMLNode &before = _editor->session()->locations()->get_state();
3568 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3569 if (_operation == CreateCDMarker) {
3570 flags = Location::IsRangeMarker | Location::IsCDMarker;
3571 _editor->cd_marker_bar_drag_rect->hide();
3574 flags = Location::IsRangeMarker;
3575 _editor->range_bar_drag_rect->hide();
3577 newloc = new Location (
3578 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3581 _editor->session()->locations()->add (newloc, true);
3582 XMLNode &after = _editor->session()->locations()->get_state();
3583 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3584 _editor->commit_reversible_command ();
3588 case CreateTransportMarker:
3589 // popup menu to pick loop or punch
3590 _editor->new_transport_marker_context_menu (&event->button, _item);
3594 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3596 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3601 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3603 if (end == max_framepos) {
3604 end = _editor->session()->current_end_frame ();
3607 if (start == max_framepos) {
3608 start = _editor->session()->current_start_frame ();
3611 switch (_editor->mouse_mode) {
3613 /* find the two markers on either side and then make the selection from it */
3614 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3618 /* find the two markers on either side of the click and make the range out of it */
3619 _editor->selection->set (start, end);
3628 _editor->stop_canvas_autoscroll ();
3632 RangeMarkerBarDrag::aborted ()
3638 RangeMarkerBarDrag::update_item (Location* location)
3640 double const x1 = _editor->frame_to_pixel (location->start());
3641 double const x2 = _editor->frame_to_pixel (location->end());
3643 _drag_rect->property_x1() = x1;
3644 _drag_rect->property_x2() = x2;
3647 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3651 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3655 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3657 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3658 Drag::start_grab (event, _editor->cursors()->zoom_out);
3661 Drag::start_grab (event, _editor->cursors()->zoom_in);
3665 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3669 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3674 framepos_t const pf = adjusted_current_frame (event);
3676 framepos_t grab = grab_frame ();
3677 _editor->snap_to_with_modifier (grab, event);
3679 /* base start and end on initial click position */
3691 _editor->zoom_rect->show();
3692 _editor->zoom_rect->raise_to_top();
3695 _editor->reposition_zoom_rect(start, end);
3697 _editor->show_verbose_time_cursor (pf, 10);
3702 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3704 if (movement_occurred) {
3705 motion (event, false);
3707 if (grab_frame() < last_pointer_frame()) {
3708 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3710 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3713 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3716 _editor->zoom_rect->hide();
3720 MouseZoomDrag::aborted ()
3722 _editor->zoom_rect->hide ();
3725 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3727 , _cumulative_dx (0)
3728 , _cumulative_dy (0)
3730 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3732 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3733 _region = &_primary->region_view ();
3734 _note_height = _region->midi_stream_view()->note_height ();
3738 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3740 Drag::start_grab (event);
3742 if (!(_was_selected = _primary->selected())) {
3744 /* tertiary-click means extend selection - we'll do that on button release,
3745 so don't add it here, because otherwise we make it hard to figure
3746 out the "extend-to" range.
3749 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3752 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3755 _region->note_selected (_primary, true);
3757 _region->unique_select (_primary);
3763 /** @return Current total drag x change in frames */
3765 NoteDrag::total_dx () const
3768 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3770 /* primary note time */
3771 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3773 /* new time of the primary note relative to the region position */
3774 frameoffset_t const st = n + dx;
3776 /* snap and return corresponding delta */
3777 return _region->snap_frame_to_frame (st) - n;
3780 /** @return Current total drag y change in notes */
3782 NoteDrag::total_dy () const
3784 /* this is `backwards' to make increasing note number go in the right direction */
3785 double const dy = _drags->current_pointer_y() - grab_y();
3790 if (abs (dy) >= _note_height) {
3792 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3794 ndy = (int8_t) floor (dy / _note_height / 2.0);
3798 /* more positive value = higher pitch and higher y-axis position on track,
3799 which is the inverse of the X-centric geometric universe
3806 NoteDrag::motion (GdkEvent *, bool)
3808 /* Total change in x and y since the start of the drag */
3809 frameoffset_t const dx = total_dx ();
3810 int8_t const dy = -total_dy ();
3812 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3813 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3814 double const tdy = dy * _note_height - _cumulative_dy;
3817 _cumulative_dx += tdx;
3818 _cumulative_dy += tdy;
3820 int8_t note_delta = total_dy();
3822 _region->move_selection (tdx, tdy, note_delta);
3825 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3826 (int) floor (_primary->note()->note() + note_delta));
3828 _editor->show_verbose_canvas_cursor_with (buf);
3833 NoteDrag::finished (GdkEvent* ev, bool moved)
3836 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3838 if (_was_selected) {
3839 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3841 _region->note_deselected (_primary);
3844 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3845 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3847 if (!extend && !add && _region->selection_size() > 1) {
3848 _region->unique_select (_primary);
3849 } else if (extend) {
3850 _region->note_selected (_primary, true, true);
3852 /* it was added during button press */
3857 _region->note_dropped (_primary, total_dx(), total_dy());
3862 NoteDrag::aborted ()
3867 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3868 : Drag (editor, item)
3870 , _nothing_to_drag (false)
3872 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3874 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3877 /* get all lines in the automation view */
3878 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3880 /* find those that overlap the ranges being dragged */
3881 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3882 while (i != lines.end ()) {
3883 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3886 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3888 /* check this range against all the AudioRanges that we are using */
3889 list<AudioRange>::const_iterator k = _ranges.begin ();
3890 while (k != _ranges.end()) {
3891 if (k->coverage (r.first, r.second) != OverlapNone) {
3897 /* add it to our list if it overlaps at all */
3898 if (k != _ranges.end()) {
3903 _lines.push_back (n);
3909 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3913 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3915 Drag::start_grab (event, cursor);
3917 /* Get line states before we start changing things */
3918 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3919 i->state = &i->line->get_state ();
3922 if (_ranges.empty()) {
3924 /* No selected time ranges: drag all points */
3925 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3926 uint32_t const N = i->line->npoints ();
3927 for (uint32_t j = 0; j < N; ++j) {
3928 i->points.push_back (i->line->nth (j));
3934 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3936 framecnt_t const half = (i->start + i->end) / 2;
3938 /* find the line that this audio range starts in */
3939 list<Line>::iterator j = _lines.begin();
3940 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3944 if (j != _lines.end()) {
3945 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3947 /* j is the line that this audio range starts in; fade into it;
3948 64 samples length plucked out of thin air.
3951 framepos_t a = i->start + 64;
3956 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3957 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3959 the_list->add (p, the_list->eval (p));
3960 j->line->add_always_in_view (p);
3961 the_list->add (q, the_list->eval (q));
3962 j->line->add_always_in_view (q);
3965 /* same thing for the end */
3968 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3972 if (j != _lines.end()) {
3973 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3975 /* j is the line that this audio range starts in; fade out of it;
3976 64 samples length plucked out of thin air.
3979 framepos_t b = i->end - 64;
3984 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3985 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
3987 the_list->add (p, the_list->eval (p));
3988 j->line->add_always_in_view (p);
3989 the_list->add (q, the_list->eval (q));
3990 j->line->add_always_in_view (q);
3994 _nothing_to_drag = true;
3996 /* Find all the points that should be dragged and put them in the relevant
3997 points lists in the Line structs.
4000 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4002 uint32_t const N = i->line->npoints ();
4003 for (uint32_t j = 0; j < N; ++j) {
4005 /* here's a control point on this line */
4006 ControlPoint* p = i->line->nth (j);
4007 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4009 /* see if it's inside a range */
4010 list<AudioRange>::const_iterator k = _ranges.begin ();
4011 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4015 if (k != _ranges.end()) {
4016 /* dragging this point */
4017 _nothing_to_drag = false;
4018 i->points.push_back (p);
4024 if (_nothing_to_drag) {
4028 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4029 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4034 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4036 if (_nothing_to_drag) {
4040 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4041 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4043 /* we are ignoring x position for this drag, so we can just pass in anything */
4044 i->line->drag_motion (0, f, true, false);
4049 AutomationRangeDrag::finished (GdkEvent* event, bool)
4051 if (_nothing_to_drag) {
4055 motion (event, false);
4056 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4057 i->line->end_drag ();
4058 i->line->clear_always_in_view ();
4061 _editor->session()->commit_reversible_command ();
4065 AutomationRangeDrag::aborted ()
4067 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4068 i->line->clear_always_in_view ();
4073 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4076 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4077 layer = v->region()->layer ();
4078 initial_y = v->get_canvas_group()->property_y ();
4079 initial_playlist = v->region()->playlist ();
4080 initial_position = v->region()->position ();
4081 initial_end = v->region()->position () + v->region()->length ();
4084 ProgramChangeDrag::ProgramChangeDrag (Editor* e, CanvasProgramChange* i, MidiRegionView* r)
4087 , _program_change (i)
4088 , _cumulative_dx (0)
4090 DEBUG_TRACE (DEBUG::Drags, "New ProgramChangeDrag\n");
4094 ProgramChangeDrag::motion (GdkEvent* ev, bool)
4096 framepos_t f = adjusted_current_frame (ev);
4097 boost::shared_ptr<Region> r = _region_view->region ();
4098 f = max (f, r->position ());
4099 f = min (f, r->last_frame ());
4101 framecnt_t const dxf = f - grab_frame();
4102 double const dxu = _editor->frame_to_unit (dxf);
4103 _program_change->move (dxu - _cumulative_dx, 0);
4104 _cumulative_dx = dxu;
4108 ProgramChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4110 if (!movement_occurred) {
4114 boost::shared_ptr<Region> r (_region_view->region ());
4116 framepos_t f = adjusted_current_frame (ev);
4117 f = max (f, r->position ());
4118 f = min (f, r->last_frame ());
4120 _region_view->move_program_change (
4121 MidiRegionView::PCEvent (_program_change->event_time(), _program_change->program(), _program_change->channel()),
4122 _region_view->frames_to_beats (f - r->position() - r->start())
4127 ProgramChangeDrag::aborted ()
4129 _program_change->move (-_cumulative_dx, 0);
4133 ProgramChangeDrag::setup_pointer_frame_offset ()
4135 boost::shared_ptr<Region> region = _region_view->region ();
4136 _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_program_change->event_time()) - region->position() + region->start();