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/audioregion.h"
33 #include "ardour/dB.h"
34 #include "ardour/midi_region.h"
35 #include "ardour/operations.h"
36 #include "ardour/region_factory.h"
37 #include "ardour/session.h"
42 #include "audio_region_view.h"
43 #include "midi_region_view.h"
44 #include "ardour_ui.h"
45 #include "gui_thread.h"
46 #include "control_point.h"
48 #include "region_gain_line.h"
49 #include "editor_drag.h"
50 #include "audio_time_axis.h"
51 #include "midi_time_axis.h"
52 #include "canvas-note.h"
53 #include "selection.h"
54 #include "midi_selection.h"
55 #include "automation_time_axis.h"
57 #include "editor_cursors.h"
58 #include "mouse_cursors.h"
59 #include "verbose_cursor.h"
62 using namespace ARDOUR;
65 using namespace Gtkmm2ext;
66 using namespace Editing;
67 using namespace ArdourCanvas;
69 using Gtkmm2ext::Keyboard;
71 double ControlPointDrag::_zero_gain_fraction = -1.0;
73 DragManager::DragManager (Editor* e)
76 , _current_pointer_frame (0)
80 DragManager::~DragManager ()
85 /** Call abort for each active drag */
91 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
96 if (!_drags.empty ()) {
97 _editor->set_follow_playhead (_old_follow_playhead, false);
106 DragManager::add (Drag* d)
108 d->set_manager (this);
109 _drags.push_back (d);
113 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
115 d->set_manager (this);
116 _drags.push_back (d);
121 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
123 /* Prevent follow playhead during the drag to be nice to the user */
124 _old_follow_playhead = _editor->follow_playhead ();
125 _editor->set_follow_playhead (false);
127 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
129 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
130 (*i)->start_grab (e, c);
134 /** Call end_grab for each active drag.
135 * @return true if any drag reported movement having occurred.
138 DragManager::end_grab (GdkEvent* e)
143 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
144 bool const t = (*i)->end_grab (e);
155 _editor->set_follow_playhead (_old_follow_playhead, false);
161 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
165 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
167 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
168 bool const t = (*i)->motion_handler (e, from_autoscroll);
179 DragManager::have_item (ArdourCanvas::Item* i) const
181 list<Drag*>::const_iterator j = _drags.begin ();
182 while (j != _drags.end() && (*j)->item () != i) {
186 return j != _drags.end ();
189 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
192 , _pointer_frame_offset (0)
193 , _move_threshold_passed (false)
194 , _raw_grab_frame (0)
196 , _last_pointer_frame (0)
202 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
208 cursor = _editor->which_grabber_cursor ();
211 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
215 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
218 cursor = _editor->which_grabber_cursor ();
221 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
223 if (Keyboard::is_button2_event (&event->button)) {
224 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
225 _y_constrained = true;
226 _x_constrained = false;
228 _y_constrained = false;
229 _x_constrained = true;
232 _x_constrained = false;
233 _y_constrained = false;
236 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
237 setup_pointer_frame_offset ();
238 _grab_frame = adjusted_frame (_raw_grab_frame, event);
239 _last_pointer_frame = _grab_frame;
240 _last_pointer_x = _grab_x;
241 _last_pointer_y = _grab_y;
243 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
247 if (_editor->session() && _editor->session()->transport_rolling()) {
250 _was_rolling = false;
253 switch (_editor->snap_type()) {
254 case SnapToRegionStart:
255 case SnapToRegionEnd:
256 case SnapToRegionSync:
257 case SnapToRegionBoundary:
258 _editor->build_region_boundary_cache ();
265 /** Call to end a drag `successfully'. Ungrabs item and calls
266 * subclass' finished() method.
268 * @param event GDK event, or 0.
269 * @return true if some movement occurred, otherwise false.
272 Drag::end_grab (GdkEvent* event)
274 _editor->stop_canvas_autoscroll ();
276 _item->ungrab (event ? event->button.time : 0);
278 finished (event, _move_threshold_passed);
280 _editor->verbose_cursor()->hide ();
282 return _move_threshold_passed;
286 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
290 if (f > _pointer_frame_offset) {
291 pos = f - _pointer_frame_offset;
295 _editor->snap_to_with_modifier (pos, event);
302 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
304 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
308 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
310 /* check to see if we have moved in any way that matters since the last motion event */
311 if (_move_threshold_passed &&
312 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
313 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
317 pair<framecnt_t, int> const threshold = move_threshold ();
319 bool const old_move_threshold_passed = _move_threshold_passed;
321 if (!from_autoscroll && !_move_threshold_passed) {
323 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
324 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
326 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
329 if (active (_editor->mouse_mode) && _move_threshold_passed) {
331 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
332 if (!from_autoscroll) {
333 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
336 motion (event, _move_threshold_passed != old_move_threshold_passed);
338 _last_pointer_x = _drags->current_pointer_x ();
339 _last_pointer_y = _drags->current_pointer_y ();
340 _last_pointer_frame = adjusted_current_frame (event);
348 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
356 aborted (_move_threshold_passed);
358 _editor->stop_canvas_autoscroll ();
359 _editor->verbose_cursor()->hide ();
363 Drag::show_verbose_cursor_time (framepos_t frame)
365 _editor->verbose_cursor()->set_time (
367 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
368 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
371 _editor->verbose_cursor()->show ();
375 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
377 _editor->verbose_cursor()->show (xoffset);
379 _editor->verbose_cursor()->set_duration (
381 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
382 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
387 Drag::show_verbose_cursor_text (string const & text)
389 _editor->verbose_cursor()->show ();
391 _editor->verbose_cursor()->set (
393 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
394 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
399 struct EditorOrderTimeAxisViewSorter {
400 bool operator() (TimeAxisView* a, TimeAxisView* b) {
401 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
402 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
404 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
408 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
412 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
414 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
415 as some of the regions we are dragging may be on such tracks.
418 TrackViewList track_views = _editor->track_views;
419 track_views.sort (EditorOrderTimeAxisViewSorter ());
421 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
422 _time_axis_views.push_back (*i);
424 TimeAxisView::Children children_list = (*i)->get_child_list ();
425 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
426 _time_axis_views.push_back (j->get());
430 /* the list of views can be empty at this point if this is a region list-insert drag
433 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
434 _views.push_back (DraggingView (*i, this));
437 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
441 RegionDrag::region_going_away (RegionView* v)
443 list<DraggingView>::iterator i = _views.begin ();
444 while (i != _views.end() && i->view != v) {
448 if (i != _views.end()) {
453 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
454 * or -1 if it is not found.
457 RegionDrag::find_time_axis_view (TimeAxisView* t) const
460 int const N = _time_axis_views.size ();
461 while (i < N && _time_axis_views[i] != t) {
472 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
473 : RegionDrag (e, i, p, v),
482 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
484 Drag::start_grab (event, cursor);
486 show_verbose_cursor_time (_last_frame_position);
488 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
489 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
490 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
494 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
496 /* compute the amount of pointer motion in frames, and where
497 the region would be if we moved it by that much.
499 *pending_region_position = adjusted_current_frame (event);
501 framepos_t sync_frame;
502 framecnt_t sync_offset;
505 sync_offset = _primary->region()->sync_offset (sync_dir);
507 /* we don't handle a sync point that lies before zero.
509 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
511 sync_frame = *pending_region_position + (sync_dir*sync_offset);
513 _editor->snap_to_with_modifier (sync_frame, event);
515 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
518 *pending_region_position = _last_frame_position;
521 if (*pending_region_position > max_framepos - _primary->region()->length()) {
522 *pending_region_position = _last_frame_position;
527 /* in locked edit mode, reverse the usual meaning of _x_constrained */
528 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
530 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
532 /* x movement since last time (in pixels) */
533 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
535 /* total x movement */
536 framecnt_t total_dx = *pending_region_position;
537 if (regions_came_from_canvas()) {
538 total_dx = total_dx - grab_frame ();
541 /* check that no regions have gone off the start of the session */
542 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
543 if ((i->view->region()->position() + total_dx) < 0) {
545 *pending_region_position = _last_frame_position;
550 _last_frame_position = *pending_region_position;
557 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
559 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
560 int const n = i->time_axis_view + delta_track;
561 if (n < 0 || n >= int (_time_axis_views.size())) {
562 /* off the top or bottom track */
566 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
567 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
568 /* not a track, or the wrong type */
572 int const l = i->layer + delta_layer;
573 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
574 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
575 If it has, the layers will be munged later anyway, so it's ok.
581 /* all regions being dragged are ok with this change */
586 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
588 assert (!_views.empty ());
590 /* Find the TimeAxisView that the pointer is now over */
591 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
593 /* Bail early if we're not over a track */
594 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
595 if (!rtv || !rtv->is_track()) {
596 _editor->verbose_cursor()->hide ();
600 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
602 /* Here's the current pointer position in terms of time axis view and layer */
603 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
604 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
606 /* Work out the change in x */
607 framepos_t pending_region_position;
608 double const x_delta = compute_x_delta (event, &pending_region_position);
610 /* Work out the change in y */
611 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
612 int delta_layer = current_pointer_layer - _last_pointer_layer;
614 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
615 /* this y movement is not allowed, so do no y movement this time */
616 delta_time_axis_view = 0;
620 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
621 /* haven't reached next snap point, and we're not switching
622 trackviews nor layers. nothing to do.
627 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
629 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
631 RegionView* rv = i->view;
633 if (rv->region()->locked()) {
639 rv->get_time_axis_view().hide_dependent_views (*rv);
641 /* Reparent to a non scrolling group so that we can keep the
642 region selection above all time axis views.
643 Reparenting means that we will have to move the region view
644 later, as the two parent groups have different coordinates.
647 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
649 rv->fake_set_opaque (true);
651 if (!rv->get_time_axis_view().hidden()) {
652 /* the track that this region view is on is hidden, so hide the region too */
653 rv->get_canvas_group()->hide ();
657 /* If we have moved tracks, we'll fudge the layer delta so that the
658 region gets moved back onto layer 0 on its new track; this avoids
659 confusion when dragging regions from non-zero layers onto different
662 int this_delta_layer = delta_layer;
663 if (delta_time_axis_view != 0) {
664 this_delta_layer = - i->layer;
667 /* The TimeAxisView that this region is now on */
668 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
671 rv->set_height (tv->view()->child_height ());
673 /* Update show/hidden status as the region view may have come from a hidden track,
674 or have moved to one.
677 rv->get_canvas_group()->hide ();
679 rv->get_canvas_group()->show ();
682 /* Update the DraggingView */
683 i->time_axis_view += delta_time_axis_view;
684 i->layer += this_delta_layer;
687 _editor->mouse_brush_insert_region (rv, pending_region_position);
692 /* Get the y coordinate of the top of the track that this region is now on */
693 tv->canvas_display()->i2w (x, y);
694 y += _editor->get_trackview_group_vertical_offset();
696 /* And adjust for the layer that it should be on */
697 StreamView* cv = tv->view ();
698 if (cv->layer_display() == Stacked) {
699 y += (cv->layers() - i->layer - 1) * cv->child_height ();
702 /* Now move the region view */
703 rv->move (x_delta, y - rv->get_canvas_group()->property_y());
706 } /* foreach region */
708 _total_x_delta += x_delta;
711 _editor->cursor_group->raise_to_top();
714 if (x_delta != 0 && !_brushing) {
715 show_verbose_cursor_time (_last_frame_position);
718 _last_pointer_time_axis_view += delta_time_axis_view;
719 _last_pointer_layer += delta_layer;
723 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
725 if (_copy && first_move) {
727 /* duplicate the regionview(s) and region(s) */
729 list<DraggingView> new_regionviews;
731 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
733 RegionView* rv = i->view;
734 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
735 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
737 const boost::shared_ptr<const Region> original = rv->region();
738 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
739 region_copy->set_position (original->position());
743 boost::shared_ptr<AudioRegion> audioregion_copy
744 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
746 nrv = new AudioRegionView (*arv, audioregion_copy);
748 boost::shared_ptr<MidiRegion> midiregion_copy
749 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
750 nrv = new MidiRegionView (*mrv, midiregion_copy);
755 nrv->get_canvas_group()->show ();
756 new_regionviews.push_back (DraggingView (nrv, this));
758 /* swap _primary to the copy */
760 if (rv == _primary) {
764 /* ..and deselect the one we copied */
766 rv->set_selected (false);
769 if (!new_regionviews.empty()) {
771 /* reflect the fact that we are dragging the copies */
773 _views = new_regionviews;
775 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
778 sync the canvas to what we think is its current state
779 without it, the canvas seems to
780 "forget" to update properly after the upcoming reparent()
781 ..only if the mouse is in rapid motion at the time of the grab.
782 something to do with regionview creation taking so long?
784 _editor->update_canvas_now();
788 RegionMotionDrag::motion (event, first_move);
792 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
794 if (!movement_occurred) {
799 /* reverse this here so that we have the correct logic to finalize
803 if (Config->get_edit_mode() == Lock) {
804 _x_constrained = !_x_constrained;
807 assert (!_views.empty ());
809 /* We might have hidden region views so that they weren't visible during the drag
810 (when they have been reparented). Now everything can be shown again, as region
811 views are back in their track parent groups.
813 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
814 i->view->get_canvas_group()->show ();
817 bool const changed_position = (_last_frame_position != _primary->region()->position());
818 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
819 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
821 _editor->update_canvas_now ();
843 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
845 RegionSelection new_views;
846 PlaylistSet modified_playlists;
847 list<RegionView*> views_to_delete;
850 /* all changes were made during motion event handlers */
852 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
856 _editor->commit_reversible_command ();
860 if (_x_constrained) {
861 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
863 _editor->begin_reversible_command (Operations::region_copy);
866 /* insert the regions into their new playlists */
867 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
869 if (i->view->region()->locked()) {
875 if (changed_position && !_x_constrained) {
876 where = i->view->region()->position() - drag_delta;
878 where = i->view->region()->position();
881 RegionView* new_view = insert_region_into_playlist (
882 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
889 new_views.push_back (new_view);
891 /* we don't need the copied RegionView any more */
892 views_to_delete.push_back (i->view);
895 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
896 because when views are deleted they are automagically removed from _views, which messes
899 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
903 /* If we've created new regions either by copying or moving
904 to a new track, we want to replace the old selection with the new ones
907 if (new_views.size() > 0) {
908 _editor->selection->set (new_views);
911 /* write commands for the accumulated diffs for all our modified playlists */
912 add_stateful_diff_commands_for_playlists (modified_playlists);
914 _editor->commit_reversible_command ();
918 RegionMoveDrag::finished_no_copy (
919 bool const changed_position,
920 bool const changed_tracks,
921 framecnt_t const drag_delta
924 RegionSelection new_views;
925 PlaylistSet modified_playlists;
926 PlaylistSet frozen_playlists;
929 /* all changes were made during motion event handlers */
930 _editor->commit_reversible_command ();
934 if (_x_constrained) {
935 _editor->begin_reversible_command (_("fixed time region drag"));
937 _editor->begin_reversible_command (Operations::region_drag);
940 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
942 RegionView* rv = i->view;
944 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
945 layer_t const dest_layer = i->layer;
947 if (rv->region()->locked()) {
954 if (changed_position && !_x_constrained) {
955 where = rv->region()->position() - drag_delta;
957 where = rv->region()->position();
960 if (changed_tracks) {
962 /* insert into new playlist */
964 RegionView* new_view = insert_region_into_playlist (
965 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
973 new_views.push_back (new_view);
975 /* remove from old playlist */
977 /* the region that used to be in the old playlist is not
978 moved to the new one - we use a copy of it. as a result,
979 any existing editor for the region should no longer be
982 rv->hide_region_editor();
983 rv->fake_set_opaque (false);
985 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
989 rv->region()->clear_changes ();
992 motion on the same track. plonk the previously reparented region
993 back to its original canvas group (its streamview).
994 No need to do anything for copies as they are fake regions which will be deleted.
997 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
998 rv->get_canvas_group()->property_y() = i->initial_y;
999 rv->get_time_axis_view().reveal_dependent_views (*rv);
1001 /* just change the model */
1003 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1005 if (dest_rtv->view()->layer_display() == Stacked) {
1006 rv->region()->set_layer (dest_layer);
1007 rv->region()->set_pending_explicit_relayer (true);
1010 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1012 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1015 playlist->freeze ();
1018 /* this movement may result in a crossfade being modified, so we need to get undo
1019 data from the playlist as well as the region.
1022 r = modified_playlists.insert (playlist);
1024 playlist->clear_changes ();
1027 rv->region()->set_position (where);
1029 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1032 if (changed_tracks) {
1034 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1035 was selected in all of them, then removing it from a playlist will have removed all
1036 trace of it from _views (i.e. there were N regions selected, we removed 1,
1037 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1038 corresponding regionview, and _views is now empty).
1040 This could have invalidated any and all iterators into _views.
1042 The heuristic we use here is: if the region selection is empty, break out of the loop
1043 here. if the region selection is not empty, then restart the loop because we know that
1044 we must have removed at least the region(view) we've just been working on as well as any
1045 that we processed on previous iterations.
1047 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1048 we can just iterate.
1052 if (_views.empty()) {
1063 /* If we've created new regions either by copying or moving
1064 to a new track, we want to replace the old selection with the new ones
1067 if (new_views.size() > 0) {
1068 _editor->selection->set (new_views);
1071 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1075 /* write commands for the accumulated diffs for all our modified playlists */
1076 add_stateful_diff_commands_for_playlists (modified_playlists);
1078 _editor->commit_reversible_command ();
1081 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1082 * @param region Region to remove.
1083 * @param playlist playlist To remove from.
1084 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1085 * that clear_changes () is only called once per playlist.
1088 RegionMoveDrag::remove_region_from_playlist (
1089 boost::shared_ptr<Region> region,
1090 boost::shared_ptr<Playlist> playlist,
1091 PlaylistSet& modified_playlists
1094 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1097 playlist->clear_changes ();
1100 playlist->remove_region (region);
1104 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1105 * clearing the playlist's diff history first if necessary.
1106 * @param region Region to insert.
1107 * @param dest_rtv Destination RouteTimeAxisView.
1108 * @param dest_layer Destination layer.
1109 * @param where Destination position.
1110 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1111 * that clear_changes () is only called once per playlist.
1112 * @return New RegionView, or 0 if no insert was performed.
1115 RegionMoveDrag::insert_region_into_playlist (
1116 boost::shared_ptr<Region> region,
1117 RouteTimeAxisView* dest_rtv,
1120 PlaylistSet& modified_playlists
1123 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1124 if (!dest_playlist) {
1128 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1129 _new_region_view = 0;
1130 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1132 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1133 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1135 dest_playlist->clear_changes ();
1138 dest_playlist->add_region (region, where);
1140 if (dest_rtv->view()->layer_display() == Stacked) {
1141 region->set_layer (dest_layer);
1142 region->set_pending_explicit_relayer (true);
1147 assert (_new_region_view);
1149 return _new_region_view;
1153 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1155 _new_region_view = rv;
1159 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1161 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1162 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1164 _editor->session()->add_command (c);
1173 RegionMoveDrag::aborted (bool movement_occurred)
1177 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1184 RegionMotionDrag::aborted (movement_occurred);
1189 RegionMotionDrag::aborted (bool)
1191 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1192 RegionView* rv = i->view;
1193 TimeAxisView* tv = &(rv->get_time_axis_view ());
1194 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1196 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1197 rv->get_canvas_group()->property_y() = 0;
1198 rv->get_time_axis_view().reveal_dependent_views (*rv);
1199 rv->fake_set_opaque (false);
1200 rv->move (-_total_x_delta, 0);
1201 rv->set_height (rtv->view()->child_height ());
1204 _editor->update_canvas_now ();
1207 /** @param b true to brush, otherwise false.
1208 * @param c true to make copies of the regions being moved, otherwise false.
1210 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1211 : RegionMotionDrag (e, i, p, v, b),
1214 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1217 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1218 if (rtv && rtv->is_track()) {
1219 speed = rtv->track()->speed ();
1222 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1226 RegionMoveDrag::setup_pointer_frame_offset ()
1228 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1231 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1232 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1234 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1236 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1237 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1239 _primary = v->view()->create_region_view (r, false, false);
1241 _primary->get_canvas_group()->show ();
1242 _primary->set_position (pos, 0);
1243 _views.push_back (DraggingView (_primary, this));
1245 _last_frame_position = pos;
1247 _item = _primary->get_canvas_group ();
1251 RegionInsertDrag::finished (GdkEvent *, bool)
1253 _editor->update_canvas_now ();
1255 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1257 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1258 _primary->get_canvas_group()->property_y() = 0;
1260 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1262 _editor->begin_reversible_command (Operations::insert_region);
1263 playlist->clear_changes ();
1264 playlist->add_region (_primary->region (), _last_frame_position);
1265 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1266 _editor->commit_reversible_command ();
1274 RegionInsertDrag::aborted (bool)
1281 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1282 : RegionMoveDrag (e, i, p, v, false, false)
1284 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1287 struct RegionSelectionByPosition {
1288 bool operator() (RegionView*a, RegionView* b) {
1289 return a->region()->position () < b->region()->position();
1294 RegionSpliceDrag::motion (GdkEvent* event, bool)
1296 /* Which trackview is this ? */
1298 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1299 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1301 /* The region motion is only processed if the pointer is over
1305 if (!tv || !tv->is_track()) {
1306 /* To make sure we hide the verbose canvas cursor when the mouse is
1307 not held over and audiotrack.
1309 _editor->verbose_cursor()->hide ();
1315 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1321 RegionSelection copy (_editor->selection->regions);
1323 RegionSelectionByPosition cmp;
1326 framepos_t const pf = adjusted_current_frame (event);
1328 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1330 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1336 boost::shared_ptr<Playlist> playlist;
1338 if ((playlist = atv->playlist()) == 0) {
1342 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1347 if (pf < (*i)->region()->last_frame() + 1) {
1351 if (pf > (*i)->region()->first_frame()) {
1357 playlist->shuffle ((*i)->region(), dir);
1362 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1364 RegionMoveDrag::finished (event, movement_occurred);
1368 RegionSpliceDrag::aborted (bool)
1373 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1375 _view (dynamic_cast<MidiTimeAxisView*> (v))
1377 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1383 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1387 _view->playlist()->freeze ();
1390 framepos_t const f = adjusted_current_frame (event);
1391 if (f < grab_frame()) {
1392 _region->set_position (f);
1395 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1396 so that if this region is duplicated, its duplicate starts on
1397 a snap point rather than 1 frame after a snap point. Otherwise things get
1398 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1399 place snapped notes at the start of the region.
1402 framecnt_t const len = abs (f - grab_frame () - 1);
1403 _region->set_length (len < 1 ? 1 : len);
1409 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1411 if (!movement_occurred) {
1414 _view->playlist()->thaw ();
1418 _editor->commit_reversible_command ();
1423 RegionCreateDrag::add_region ()
1425 if (_editor->session()) {
1426 const TempoMap& map (_editor->session()->tempo_map());
1427 framecnt_t pos = grab_frame();
1428 const Meter& m = map.meter_at (pos);
1429 /* not that the frame rate used here can be affected by pull up/down which
1432 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1433 _region = _view->add_region (grab_frame(), len, false);
1438 RegionCreateDrag::aborted (bool)
1441 _view->playlist()->thaw ();
1447 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1451 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1455 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1457 Gdk::Cursor* cursor;
1458 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1459 float x_fraction = cnote->mouse_x_fraction ();
1461 if (x_fraction > 0.0 && x_fraction < 0.25) {
1462 cursor = _editor->cursors()->left_side_trim;
1464 cursor = _editor->cursors()->right_side_trim;
1467 Drag::start_grab (event, cursor);
1469 region = &cnote->region_view();
1471 double const region_start = region->get_position_pixels();
1472 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1474 if (grab_x() <= middle_point) {
1475 cursor = _editor->cursors()->left_side_trim;
1478 cursor = _editor->cursors()->right_side_trim;
1482 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1484 if (event->motion.state & Keyboard::PrimaryModifier) {
1490 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1492 if (ms.size() > 1) {
1493 /* has to be relative, may make no sense otherwise */
1497 /* select this note; if it is already selected, preserve the existing selection,
1498 otherwise make this note the only one selected.
1500 region->note_selected (cnote, cnote->selected ());
1502 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1503 MidiRegionSelection::iterator next;
1506 (*r)->begin_resizing (at_front);
1512 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1514 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1515 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1516 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1521 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1523 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1524 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1525 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1530 NoteResizeDrag::aborted (bool)
1532 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1533 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1534 (*r)->abort_resizing ();
1538 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1541 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1545 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1551 RegionGainDrag::finished (GdkEvent *, bool)
1557 RegionGainDrag::aborted (bool)
1562 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1563 : RegionDrag (e, i, p, v)
1565 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1569 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1572 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1573 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1575 if (tv && tv->is_track()) {
1576 speed = tv->track()->speed();
1579 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1580 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1581 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1583 framepos_t const pf = adjusted_current_frame (event);
1585 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1586 /* Move the contents of the region around without changing the region bounds */
1587 _operation = ContentsTrim;
1588 Drag::start_grab (event, _editor->cursors()->trimmer);
1590 /* These will get overridden for a point trim.*/
1591 if (pf < (region_start + region_length/2)) {
1592 /* closer to front */
1593 _operation = StartTrim;
1594 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1597 _operation = EndTrim;
1598 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1602 switch (_operation) {
1604 show_verbose_cursor_time (region_start);
1605 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1606 i->view->trim_front_starting ();
1610 show_verbose_cursor_time (region_end);
1613 show_verbose_cursor_time (pf);
1617 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1618 i->view->region()->suspend_property_changes ();
1623 TrimDrag::motion (GdkEvent* event, bool first_move)
1625 RegionView* rv = _primary;
1628 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1629 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1630 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1632 if (tv && tv->is_track()) {
1633 speed = tv->track()->speed();
1636 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1642 switch (_operation) {
1644 trim_type = "Region start trim";
1647 trim_type = "Region end trim";
1650 trim_type = "Region content trim";
1654 _editor->begin_reversible_command (trim_type);
1656 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1657 RegionView* rv = i->view;
1658 rv->fake_set_opaque (false);
1659 rv->enable_display (false);
1660 rv->region()->playlist()->clear_owned_changes ();
1662 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1665 arv->temporarily_hide_envelope ();
1668 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1669 insert_result = _editor->motion_frozen_playlists.insert (pl);
1671 if (insert_result.second) {
1677 bool non_overlap_trim = false;
1679 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1680 non_overlap_trim = true;
1683 switch (_operation) {
1685 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1686 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1691 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1692 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1698 bool swap_direction = false;
1700 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1701 swap_direction = true;
1704 framecnt_t frame_delta = 0;
1706 bool left_direction = false;
1707 if (last_pointer_frame() > adjusted_current_frame(event)) {
1708 left_direction = true;
1711 if (left_direction) {
1712 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1714 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1717 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1718 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1724 switch (_operation) {
1726 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1729 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1732 show_verbose_cursor_time (adjusted_current_frame (event));
1739 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1741 if (movement_occurred) {
1742 motion (event, false);
1744 /* This must happen before the region's StatefulDiffCommand is created, as it may
1745 `correct' (ahem) the region's _start from being negative to being zero. It
1746 needs to be zero in the undo record.
1748 if (_operation == StartTrim) {
1749 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1750 i->view->trim_front_ending ();
1754 if (!_editor->selection->selected (_primary)) {
1755 _primary->thaw_after_trim ();
1758 set<boost::shared_ptr<Playlist> > diffed_playlists;
1760 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1761 i->view->thaw_after_trim ();
1762 i->view->enable_display (true);
1763 i->view->fake_set_opaque (true);
1765 /* Trimming one region may affect others on the playlist, so we need
1766 to get undo Commands from the whole playlist rather than just the
1767 region. Use diffed_playlists to make sure we don't diff a given
1768 playlist more than once.
1770 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1771 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1772 vector<Command*> cmds;
1774 _editor->session()->add_commands (cmds);
1775 diffed_playlists.insert (p);
1779 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1783 _editor->motion_frozen_playlists.clear ();
1784 _editor->commit_reversible_command();
1787 /* no mouse movement */
1788 _editor->point_trim (event, adjusted_current_frame (event));
1791 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1792 if (_operation == StartTrim) {
1793 i->view->trim_front_ending ();
1796 i->view->region()->resume_property_changes ();
1801 TrimDrag::aborted (bool movement_occurred)
1803 /* Our motion method is changing model state, so use the Undo system
1804 to cancel. Perhaps not ideal, as this will leave an Undo point
1805 behind which may be slightly odd from the user's point of view.
1810 if (movement_occurred) {
1814 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1815 i->view->region()->resume_property_changes ();
1820 TrimDrag::setup_pointer_frame_offset ()
1822 list<DraggingView>::iterator i = _views.begin ();
1823 while (i != _views.end() && i->view != _primary) {
1827 if (i == _views.end()) {
1831 switch (_operation) {
1833 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1836 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1843 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1847 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1849 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1854 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1857 // create a dummy marker for visual representation of moving the copy.
1858 // The actual copying is not done before we reach the finish callback.
1860 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1862 MeterMarker* new_marker = new MeterMarker (
1864 *_editor->meter_group,
1865 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1867 *new MeterSection (_marker->meter())
1870 _item = &new_marker->the_item ();
1871 _marker = new_marker;
1875 MetricSection& section (_marker->meter());
1877 if (!section.movable()) {
1883 Drag::start_grab (event, cursor);
1885 show_verbose_cursor_time (adjusted_current_frame(event));
1889 MeterMarkerDrag::setup_pointer_frame_offset ()
1891 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1895 MeterMarkerDrag::motion (GdkEvent* event, bool)
1897 framepos_t const pf = adjusted_current_frame (event);
1899 _marker->set_position (pf);
1901 show_verbose_cursor_time (pf);
1905 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1907 if (!movement_occurred) {
1911 motion (event, false);
1913 Timecode::BBT_Time when;
1915 TempoMap& map (_editor->session()->tempo_map());
1916 map.bbt_time (last_pointer_frame(), when);
1918 if (_copy == true) {
1919 _editor->begin_reversible_command (_("copy meter mark"));
1920 XMLNode &before = map.get_state();
1921 map.add_meter (_marker->meter(), when);
1922 XMLNode &after = map.get_state();
1923 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1924 _editor->commit_reversible_command ();
1926 // delete the dummy marker we used for visual representation of copying.
1927 // a new visual marker will show up automatically.
1930 _editor->begin_reversible_command (_("move meter mark"));
1931 XMLNode &before = map.get_state();
1932 map.move_meter (_marker->meter(), when);
1933 XMLNode &after = map.get_state();
1934 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1935 _editor->commit_reversible_command ();
1940 MeterMarkerDrag::aborted (bool)
1942 _marker->set_position (_marker->meter().frame ());
1945 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1949 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1951 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1956 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1960 // create a dummy marker for visual representation of moving the copy.
1961 // The actual copying is not done before we reach the finish callback.
1963 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1965 TempoMarker* new_marker = new TempoMarker (
1967 *_editor->tempo_group,
1968 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1970 *new TempoSection (_marker->tempo())
1973 _item = &new_marker->the_item ();
1974 _marker = new_marker;
1978 Drag::start_grab (event, cursor);
1980 show_verbose_cursor_time (adjusted_current_frame (event));
1984 TempoMarkerDrag::setup_pointer_frame_offset ()
1986 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1990 TempoMarkerDrag::motion (GdkEvent* event, bool)
1992 framepos_t const pf = adjusted_current_frame (event);
1993 _marker->set_position (pf);
1994 show_verbose_cursor_time (pf);
1998 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2000 if (!movement_occurred) {
2004 motion (event, false);
2006 Timecode::BBT_Time when;
2008 TempoMap& map (_editor->session()->tempo_map());
2009 map.bbt_time (last_pointer_frame(), when);
2011 if (_copy == true) {
2012 _editor->begin_reversible_command (_("copy tempo mark"));
2013 XMLNode &before = map.get_state();
2014 map.add_tempo (_marker->tempo(), when);
2015 XMLNode &after = map.get_state();
2016 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2017 _editor->commit_reversible_command ();
2019 // delete the dummy marker we used for visual representation of copying.
2020 // a new visual marker will show up automatically.
2023 _editor->begin_reversible_command (_("move tempo mark"));
2024 XMLNode &before = map.get_state();
2025 map.move_tempo (_marker->tempo(), when);
2026 XMLNode &after = map.get_state();
2027 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2028 _editor->commit_reversible_command ();
2033 TempoMarkerDrag::aborted (bool)
2035 _marker->set_position (_marker->tempo().frame());
2038 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2042 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2045 /** Do all the things we do when dragging the playhead to make it look as though
2046 * we have located, without actually doing the locate (because that would cause
2047 * the diskstream buffers to be refilled, which is too slow).
2050 CursorDrag::fake_locate (framepos_t t)
2052 _editor->playhead_cursor->set_position (t);
2054 Session* s = _editor->session ();
2055 if (s->timecode_transmission_suspended ()) {
2056 framepos_t const f = _editor->playhead_cursor->current_frame;
2057 s->send_mmc_locate (f);
2058 s->send_full_time_code (f);
2061 show_verbose_cursor_time (t);
2062 _editor->UpdateAllTransportClocks (t);
2066 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2068 Drag::start_grab (event, c);
2070 _grab_zoom = _editor->frames_per_unit;
2072 framepos_t where = _editor->event_frame (event, 0, 0);
2073 _editor->snap_to_with_modifier (where, event);
2075 _editor->_dragging_playhead = true;
2077 Session* s = _editor->session ();
2080 if (_was_rolling && _stop) {
2084 if (s->is_auditioning()) {
2085 s->cancel_audition ();
2088 s->request_suspend_timecode_transmission ();
2089 while (!s->timecode_transmission_suspended ()) {
2090 /* twiddle our thumbs */
2094 fake_locate (where);
2098 CursorDrag::motion (GdkEvent* event, bool)
2100 framepos_t const adjusted_frame = adjusted_current_frame (event);
2101 if (adjusted_frame != last_pointer_frame()) {
2102 fake_locate (adjusted_frame);
2104 _editor->update_canvas_now ();
2110 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2112 _editor->_dragging_playhead = false;
2114 if (!movement_occurred && _stop) {
2118 motion (event, false);
2120 Session* s = _editor->session ();
2122 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2123 _editor->_pending_locate_request = true;
2124 s->request_resume_timecode_transmission ();
2129 CursorDrag::aborted (bool)
2131 if (_editor->_dragging_playhead) {
2132 _editor->session()->request_resume_timecode_transmission ();
2133 _editor->_dragging_playhead = false;
2136 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2139 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2140 : RegionDrag (e, i, p, v)
2142 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2146 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2148 Drag::start_grab (event, cursor);
2150 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2151 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2153 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2155 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2159 FadeInDrag::setup_pointer_frame_offset ()
2161 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2162 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2163 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2167 FadeInDrag::motion (GdkEvent* event, bool)
2169 framecnt_t fade_length;
2171 framepos_t const pos = adjusted_current_frame (event);
2173 boost::shared_ptr<Region> region = _primary->region ();
2175 if (pos < (region->position() + 64)) {
2176 fade_length = 64; // this should be a minimum defined somewhere
2177 } else if (pos > region->last_frame()) {
2178 fade_length = region->length();
2180 fade_length = pos - region->position();
2183 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2185 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2191 tmp->reset_fade_in_shape_width (fade_length);
2192 tmp->show_fade_line((framecnt_t) fade_length);
2195 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2199 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2201 if (!movement_occurred) {
2205 framecnt_t fade_length;
2207 framepos_t const pos = adjusted_current_frame (event);
2209 boost::shared_ptr<Region> region = _primary->region ();
2211 if (pos < (region->position() + 64)) {
2212 fade_length = 64; // this should be a minimum defined somewhere
2213 } else if (pos > region->last_frame()) {
2214 fade_length = region->length();
2216 fade_length = pos - region->position();
2219 _editor->begin_reversible_command (_("change fade in length"));
2221 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2223 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2229 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2230 XMLNode &before = alist->get_state();
2232 tmp->audio_region()->set_fade_in_length (fade_length);
2233 tmp->audio_region()->set_fade_in_active (true);
2234 tmp->hide_fade_line();
2236 XMLNode &after = alist->get_state();
2237 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2240 _editor->commit_reversible_command ();
2244 FadeInDrag::aborted (bool)
2246 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2247 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2253 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2254 tmp->hide_fade_line();
2258 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2259 : RegionDrag (e, i, p, v)
2261 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2265 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2267 Drag::start_grab (event, cursor);
2269 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2270 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2272 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2274 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2278 FadeOutDrag::setup_pointer_frame_offset ()
2280 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2281 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2282 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2286 FadeOutDrag::motion (GdkEvent* event, bool)
2288 framecnt_t fade_length;
2290 framepos_t const pos = adjusted_current_frame (event);
2292 boost::shared_ptr<Region> region = _primary->region ();
2294 if (pos > (region->last_frame() - 64)) {
2295 fade_length = 64; // this should really be a minimum fade defined somewhere
2297 else if (pos < region->position()) {
2298 fade_length = region->length();
2301 fade_length = region->last_frame() - pos;
2304 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2306 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2312 tmp->reset_fade_out_shape_width (fade_length);
2313 tmp->show_fade_line(region->length() - fade_length);
2316 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2320 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2322 if (!movement_occurred) {
2326 framecnt_t fade_length;
2328 framepos_t const pos = adjusted_current_frame (event);
2330 boost::shared_ptr<Region> region = _primary->region ();
2332 if (pos > (region->last_frame() - 64)) {
2333 fade_length = 64; // this should really be a minimum fade defined somewhere
2335 else if (pos < region->position()) {
2336 fade_length = region->length();
2339 fade_length = region->last_frame() - pos;
2342 _editor->begin_reversible_command (_("change fade out length"));
2344 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2346 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2352 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2353 XMLNode &before = alist->get_state();
2355 tmp->audio_region()->set_fade_out_length (fade_length);
2356 tmp->audio_region()->set_fade_out_active (true);
2357 tmp->hide_fade_line();
2359 XMLNode &after = alist->get_state();
2360 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2363 _editor->commit_reversible_command ();
2367 FadeOutDrag::aborted (bool)
2369 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2370 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2376 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2377 tmp->hide_fade_line();
2381 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2384 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2386 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2389 _points.push_back (Gnome::Art::Point (0, 0));
2390 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2393 MarkerDrag::~MarkerDrag ()
2395 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2401 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2403 Drag::start_grab (event, cursor);
2407 Location *location = _editor->find_location_from_marker (_marker, is_start);
2408 _editor->_dragging_edit_point = true;
2410 update_item (location);
2412 // _drag_line->show();
2413 // _line->raise_to_top();
2416 show_verbose_cursor_time (location->start());
2418 show_verbose_cursor_time (location->end());
2421 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2424 case Selection::Toggle:
2425 _editor->selection->toggle (_marker);
2427 case Selection::Set:
2428 if (!_editor->selection->selected (_marker)) {
2429 _editor->selection->set (_marker);
2432 case Selection::Extend:
2434 Locations::LocationList ll;
2435 list<Marker*> to_add;
2437 _editor->selection->markers.range (s, e);
2438 s = min (_marker->position(), s);
2439 e = max (_marker->position(), e);
2442 if (e < max_framepos) {
2445 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2446 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2447 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2450 to_add.push_back (lm->start);
2453 to_add.push_back (lm->end);
2457 if (!to_add.empty()) {
2458 _editor->selection->add (to_add);
2462 case Selection::Add:
2463 _editor->selection->add (_marker);
2467 /* Set up copies for us to manipulate during the drag */
2469 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2470 Location* l = _editor->find_location_from_marker (*i, is_start);
2471 _copied_locations.push_back (new Location (*l));
2476 MarkerDrag::setup_pointer_frame_offset ()
2479 Location *location = _editor->find_location_from_marker (_marker, is_start);
2480 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2484 MarkerDrag::motion (GdkEvent* event, bool)
2486 framecnt_t f_delta = 0;
2488 bool move_both = false;
2490 Location *real_location;
2491 Location *copy_location = 0;
2493 framepos_t const newframe = adjusted_current_frame (event);
2495 framepos_t next = newframe;
2497 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2501 MarkerSelection::iterator i;
2502 list<Location*>::iterator x;
2504 /* find the marker we're dragging, and compute the delta */
2506 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2507 x != _copied_locations.end() && i != _editor->selection->markers.end();
2513 if (marker == _marker) {
2515 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2520 if (real_location->is_mark()) {
2521 f_delta = newframe - copy_location->start();
2525 switch (marker->type()) {
2526 case Marker::SessionStart:
2527 case Marker::RangeStart:
2528 case Marker::LoopStart:
2529 case Marker::PunchIn:
2530 f_delta = newframe - copy_location->start();
2533 case Marker::SessionEnd:
2534 case Marker::RangeEnd:
2535 case Marker::LoopEnd:
2536 case Marker::PunchOut:
2537 f_delta = newframe - copy_location->end();
2540 /* what kind of marker is this ? */
2548 if (i == _editor->selection->markers.end()) {
2549 /* hmm, impossible - we didn't find the dragged marker */
2553 /* now move them all */
2555 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2556 x != _copied_locations.end() && i != _editor->selection->markers.end();
2562 /* call this to find out if its the start or end */
2564 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2568 if (real_location->locked()) {
2572 if (copy_location->is_mark()) {
2576 copy_location->set_start (copy_location->start() + f_delta);
2580 framepos_t new_start = copy_location->start() + f_delta;
2581 framepos_t new_end = copy_location->end() + f_delta;
2583 if (is_start) { // start-of-range marker
2586 copy_location->set_start (new_start);
2587 copy_location->set_end (new_end);
2588 } else if (new_start < copy_location->end()) {
2589 copy_location->set_start (new_start);
2590 } else if (newframe > 0) {
2591 _editor->snap_to (next, 1, true);
2592 copy_location->set_end (next);
2593 copy_location->set_start (newframe);
2596 } else { // end marker
2599 copy_location->set_end (new_end);
2600 copy_location->set_start (new_start);
2601 } else if (new_end > copy_location->start()) {
2602 copy_location->set_end (new_end);
2603 } else if (newframe > 0) {
2604 _editor->snap_to (next, -1, true);
2605 copy_location->set_start (next);
2606 copy_location->set_end (newframe);
2611 update_item (copy_location);
2613 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2616 lm->set_position (copy_location->start(), copy_location->end());
2620 assert (!_copied_locations.empty());
2622 show_verbose_cursor_time (newframe);
2625 _editor->update_canvas_now ();
2630 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2632 if (!movement_occurred) {
2634 /* just a click, do nothing but finish
2635 off the selection process
2638 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2641 case Selection::Set:
2642 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2643 _editor->selection->set (_marker);
2647 case Selection::Toggle:
2648 case Selection::Extend:
2649 case Selection::Add:
2656 _editor->_dragging_edit_point = false;
2658 _editor->begin_reversible_command ( _("move marker") );
2659 XMLNode &before = _editor->session()->locations()->get_state();
2661 MarkerSelection::iterator i;
2662 list<Location*>::iterator x;
2665 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2666 x != _copied_locations.end() && i != _editor->selection->markers.end();
2669 Location * location = _editor->find_location_from_marker (*i, is_start);
2673 if (location->locked()) {
2677 if (location->is_mark()) {
2678 location->set_start ((*x)->start());
2680 location->set ((*x)->start(), (*x)->end());
2685 XMLNode &after = _editor->session()->locations()->get_state();
2686 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2687 _editor->commit_reversible_command ();
2691 MarkerDrag::aborted (bool)
2697 MarkerDrag::update_item (Location*)
2702 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2704 _cumulative_x_drag (0),
2705 _cumulative_y_drag (0)
2707 if (_zero_gain_fraction < 0.0) {
2708 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2711 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2713 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2719 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2721 Drag::start_grab (event, _editor->cursors()->fader);
2723 // start the grab at the center of the control point so
2724 // the point doesn't 'jump' to the mouse after the first drag
2725 _fixed_grab_x = _point->get_x();
2726 _fixed_grab_y = _point->get_y();
2728 float const fraction = 1 - (_point->get_y() / _point->line().height());
2730 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2732 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2733 event->button.x + 10, event->button.y + 10);
2735 _editor->verbose_cursor()->show ();
2739 ControlPointDrag::motion (GdkEvent* event, bool)
2741 double dx = _drags->current_pointer_x() - last_pointer_x();
2742 double dy = _drags->current_pointer_y() - last_pointer_y();
2744 if (event->button.state & Keyboard::SecondaryModifier) {
2749 /* coordinate in pixels relative to the start of the region (for region-based automation)
2750 or track (for track-based automation) */
2751 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2752 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2754 // calculate zero crossing point. back off by .01 to stay on the
2755 // positive side of zero
2756 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2758 // make sure we hit zero when passing through
2759 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2763 if (_x_constrained) {
2766 if (_y_constrained) {
2770 _cumulative_x_drag = cx - _fixed_grab_x;
2771 _cumulative_y_drag = cy - _fixed_grab_y;
2775 cy = min ((double) _point->line().height(), cy);
2777 framepos_t cx_frames = _editor->unit_to_frame (cx);
2779 if (!_x_constrained) {
2780 _editor->snap_to_with_modifier (cx_frames, event);
2783 cx_frames = min (cx_frames, _point->line().maximum_time());
2785 float const fraction = 1.0 - (cy / _point->line().height());
2787 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2789 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2791 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2795 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2797 if (!movement_occurred) {
2801 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2802 _editor->reset_point_selection ();
2806 motion (event, false);
2809 _point->line().end_drag ();
2810 _editor->session()->commit_reversible_command ();
2814 ControlPointDrag::aborted (bool)
2816 _point->line().reset ();
2820 ControlPointDrag::active (Editing::MouseMode m)
2822 if (m == Editing::MouseGain) {
2823 /* always active in mouse gain */
2827 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2828 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2831 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2834 _cumulative_y_drag (0)
2836 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2840 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2842 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2845 _item = &_line->grab_item ();
2847 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2848 origin, and ditto for y.
2851 double cx = event->button.x;
2852 double cy = event->button.y;
2854 _line->parent_group().w2i (cx, cy);
2856 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2861 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2862 /* no adjacent points */
2866 Drag::start_grab (event, _editor->cursors()->fader);
2868 /* store grab start in parent frame */
2873 double fraction = 1.0 - (cy / _line->height());
2875 _line->start_drag_line (before, after, fraction);
2877 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2878 event->button.x + 10, event->button.y + 10);
2880 _editor->verbose_cursor()->show ();
2884 LineDrag::motion (GdkEvent* event, bool)
2886 double dy = _drags->current_pointer_y() - last_pointer_y();
2888 if (event->button.state & Keyboard::SecondaryModifier) {
2892 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2894 _cumulative_y_drag = cy - _fixed_grab_y;
2897 cy = min ((double) _line->height(), cy);
2899 double const fraction = 1.0 - (cy / _line->height());
2903 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2909 /* we are ignoring x position for this drag, so we can just pass in anything */
2910 _line->drag_motion (0, fraction, true, push);
2912 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
2916 LineDrag::finished (GdkEvent* event, bool)
2918 motion (event, false);
2920 _editor->session()->commit_reversible_command ();
2924 LineDrag::aborted (bool)
2929 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2932 _cumulative_x_drag (0)
2934 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2938 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2940 Drag::start_grab (event);
2942 _line = reinterpret_cast<Line*> (_item);
2945 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2947 double cx = event->button.x;
2948 double cy = event->button.y;
2950 _item->property_parent().get_value()->w2i(cx, cy);
2952 /* store grab start in parent frame */
2953 _region_view_grab_x = cx;
2955 _before = *(float*) _item->get_data ("position");
2957 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2959 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2963 FeatureLineDrag::motion (GdkEvent*, bool)
2965 double dx = _drags->current_pointer_x() - last_pointer_x();
2967 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2969 _cumulative_x_drag += dx;
2971 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2980 ArdourCanvas::Points points;
2982 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
2984 _line->get_bounds(x1, y2, x2, y2);
2986 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
2987 points.push_back(Gnome::Art::Point(cx, y2 - y1));
2989 _line->property_points() = points;
2991 float *pos = new float;
2994 _line->set_data ("position", pos);
3000 FeatureLineDrag::finished (GdkEvent*, bool)
3002 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3003 _arv->update_transient(_before, _before);
3007 FeatureLineDrag::aborted (bool)
3012 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3015 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3019 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3021 Drag::start_grab (event);
3022 show_verbose_cursor_time (adjusted_current_frame (event));
3026 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3033 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3035 framepos_t grab = grab_frame ();
3036 if (Config->get_rubberbanding_snaps_to_grid ()) {
3037 _editor->snap_to_with_modifier (grab, event);
3040 /* base start and end on initial click position */
3050 if (_drags->current_pointer_y() < grab_y()) {
3051 y1 = _drags->current_pointer_y();
3054 y2 = _drags->current_pointer_y();
3059 if (start != end || y1 != y2) {
3061 double x1 = _editor->frame_to_pixel (start);
3062 double x2 = _editor->frame_to_pixel (end);
3064 _editor->rubberband_rect->property_x1() = x1;
3065 _editor->rubberband_rect->property_y1() = y1;
3066 _editor->rubberband_rect->property_x2() = x2;
3067 _editor->rubberband_rect->property_y2() = y2;
3069 _editor->rubberband_rect->show();
3070 _editor->rubberband_rect->raise_to_top();
3072 show_verbose_cursor_time (pf);
3074 do_select_things (event, true);
3079 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3084 if (grab_frame() < last_pointer_frame()) {
3086 x2 = last_pointer_frame ();
3089 x1 = last_pointer_frame ();
3095 if (_drags->current_pointer_y() < grab_y()) {
3096 y1 = _drags->current_pointer_y();
3099 y2 = _drags->current_pointer_y();
3103 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3107 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3109 if (movement_occurred) {
3111 motion (event, false);
3112 do_select_things (event, false);
3120 _editor->rubberband_rect->hide();
3124 RubberbandSelectDrag::aborted (bool)
3126 _editor->rubberband_rect->hide ();
3129 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3130 : RegionDrag (e, i, p, v)
3132 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3136 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3138 Drag::start_grab (event, cursor);
3140 show_verbose_cursor_time (adjusted_current_frame (event));
3144 TimeFXDrag::motion (GdkEvent* event, bool)
3146 RegionView* rv = _primary;
3148 framepos_t const pf = adjusted_current_frame (event);
3150 if (pf > rv->region()->position()) {
3151 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3154 show_verbose_cursor_time (pf);
3158 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3160 _primary->get_time_axis_view().hide_timestretch ();
3162 if (!movement_occurred) {
3166 if (last_pointer_frame() < _primary->region()->position()) {
3167 /* backwards drag of the left edge - not usable */
3171 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3173 float percentage = (double) newlen / (double) _primary->region()->length();
3175 #ifndef USE_RUBBERBAND
3176 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3177 if (_primary->region()->data_type() == DataType::AUDIO) {
3178 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3182 // XXX how do timeFX on multiple regions ?
3187 if (_editor->time_stretch (rs, percentage) == -1) {
3188 error << _("An error occurred while executing time stretch operation") << endmsg;
3193 TimeFXDrag::aborted (bool)
3195 _primary->get_time_axis_view().hide_timestretch ();
3198 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3201 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3205 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3207 Drag::start_grab (event);
3211 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3213 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3217 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3219 if (movement_occurred && _editor->session()) {
3220 /* make sure we stop */
3221 _editor->session()->request_transport_speed (0.0);
3226 ScrubDrag::aborted (bool)
3231 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3235 , _original_pointer_time_axis (-1)
3236 , _last_pointer_time_axis (-1)
3238 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3242 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3244 if (_editor->session() == 0) {
3248 Gdk::Cursor* cursor = 0;
3250 switch (_operation) {
3251 case CreateSelection:
3252 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3257 cursor = _editor->cursors()->selector;
3258 Drag::start_grab (event, cursor);
3261 case SelectionStartTrim:
3262 if (_editor->clicked_axisview) {
3263 _editor->clicked_axisview->order_selection_trims (_item, true);
3265 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3268 case SelectionEndTrim:
3269 if (_editor->clicked_axisview) {
3270 _editor->clicked_axisview->order_selection_trims (_item, false);
3272 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3276 Drag::start_grab (event, cursor);
3280 if (_operation == SelectionMove) {
3281 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3283 show_verbose_cursor_time (adjusted_current_frame (event));
3286 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3290 SelectionDrag::setup_pointer_frame_offset ()
3292 switch (_operation) {
3293 case CreateSelection:
3294 _pointer_frame_offset = 0;
3297 case SelectionStartTrim:
3299 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3302 case SelectionEndTrim:
3303 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3309 SelectionDrag::motion (GdkEvent* event, bool first_move)
3311 framepos_t start = 0;
3315 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3316 if (pending_time_axis.first == 0) {
3320 framepos_t const pending_position = adjusted_current_frame (event);
3322 /* only alter selection if things have changed */
3324 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3328 switch (_operation) {
3329 case CreateSelection:
3331 framepos_t grab = grab_frame ();
3334 _editor->snap_to (grab);
3337 if (pending_position < grab_frame()) {
3338 start = pending_position;
3341 end = pending_position;
3345 /* first drag: Either add to the selection
3346 or create a new selection
3352 /* adding to the selection */
3353 _editor->set_selected_track_as_side_effect (Selection::Add);
3354 //_editor->selection->add (_editor->clicked_axisview);
3355 _editor->clicked_selection = _editor->selection->add (start, end);
3360 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3361 //_editor->selection->set (_editor->clicked_axisview);
3362 _editor->set_selected_track_as_side_effect (Selection::Set);
3365 _editor->clicked_selection = _editor->selection->set (start, end);
3369 /* select the track that we're in */
3370 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3371 // _editor->set_selected_track_as_side_effect (Selection::Add);
3372 _editor->selection->add (pending_time_axis.first);
3373 _added_time_axes.push_back (pending_time_axis.first);
3376 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3377 tracks that we selected in the first place.
3380 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3381 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3383 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3384 while (i != _added_time_axes.end()) {
3386 list<TimeAxisView*>::iterator tmp = i;
3389 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3390 _editor->selection->remove (*i);
3391 _added_time_axes.remove (*i);
3400 case SelectionStartTrim:
3402 start = _editor->selection->time[_editor->clicked_selection].start;
3403 end = _editor->selection->time[_editor->clicked_selection].end;
3405 if (pending_position > end) {
3408 start = pending_position;
3412 case SelectionEndTrim:
3414 start = _editor->selection->time[_editor->clicked_selection].start;
3415 end = _editor->selection->time[_editor->clicked_selection].end;
3417 if (pending_position < start) {
3420 end = pending_position;
3427 start = _editor->selection->time[_editor->clicked_selection].start;
3428 end = _editor->selection->time[_editor->clicked_selection].end;
3430 length = end - start;
3432 start = pending_position;
3433 _editor->snap_to (start);
3435 end = start + length;
3440 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3441 _editor->start_canvas_autoscroll (1, 0);
3445 _editor->selection->replace (_editor->clicked_selection, start, end);
3448 if (_operation == SelectionMove) {
3449 show_verbose_cursor_time(start);
3451 show_verbose_cursor_time(pending_position);
3456 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3458 Session* s = _editor->session();
3460 if (movement_occurred) {
3461 motion (event, false);
3462 /* XXX this is not object-oriented programming at all. ick */
3463 if (_editor->selection->time.consolidate()) {
3464 _editor->selection->TimeChanged ();
3467 /* XXX what if its a music time selection? */
3468 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3469 s->request_play_range (&_editor->selection->time, true);
3474 /* just a click, no pointer movement.*/
3476 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3477 _editor->selection->clear_time();
3480 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3481 _editor->selection->set (_editor->clicked_axisview);
3484 if (s && s->get_play_range () && s->transport_rolling()) {
3485 s->request_stop (false, false);
3490 _editor->stop_canvas_autoscroll ();
3494 SelectionDrag::aborted (bool)
3499 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3504 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3506 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3507 physical_screen_height (_editor->get_window()));
3508 _drag_rect->hide ();
3510 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3511 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3515 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3517 if (_editor->session() == 0) {
3521 Gdk::Cursor* cursor = 0;
3523 if (!_editor->temp_location) {
3524 _editor->temp_location = new Location (*_editor->session());
3527 switch (_operation) {
3528 case CreateRangeMarker:
3529 case CreateTransportMarker:
3530 case CreateCDMarker:
3532 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3537 cursor = _editor->cursors()->selector;
3541 Drag::start_grab (event, cursor);
3543 show_verbose_cursor_time (adjusted_current_frame (event));
3547 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3549 framepos_t start = 0;
3551 ArdourCanvas::SimpleRect *crect;
3553 switch (_operation) {
3554 case CreateRangeMarker:
3555 crect = _editor->range_bar_drag_rect;
3557 case CreateTransportMarker:
3558 crect = _editor->transport_bar_drag_rect;
3560 case CreateCDMarker:
3561 crect = _editor->cd_marker_bar_drag_rect;
3564 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3569 framepos_t const pf = adjusted_current_frame (event);
3571 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3572 framepos_t grab = grab_frame ();
3573 _editor->snap_to (grab);
3575 if (pf < grab_frame()) {
3583 /* first drag: Either add to the selection
3584 or create a new selection.
3589 _editor->temp_location->set (start, end);
3593 update_item (_editor->temp_location);
3595 //_drag_rect->raise_to_top();
3600 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3601 _editor->start_canvas_autoscroll (1, 0);
3605 _editor->temp_location->set (start, end);
3607 double x1 = _editor->frame_to_pixel (start);
3608 double x2 = _editor->frame_to_pixel (end);
3609 crect->property_x1() = x1;
3610 crect->property_x2() = x2;
3612 update_item (_editor->temp_location);
3615 show_verbose_cursor_time (pf);
3620 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3622 Location * newloc = 0;
3626 if (movement_occurred) {
3627 motion (event, false);
3630 switch (_operation) {
3631 case CreateRangeMarker:
3632 case CreateCDMarker:
3634 _editor->begin_reversible_command (_("new range marker"));
3635 XMLNode &before = _editor->session()->locations()->get_state();
3636 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3637 if (_operation == CreateCDMarker) {
3638 flags = Location::IsRangeMarker | Location::IsCDMarker;
3639 _editor->cd_marker_bar_drag_rect->hide();
3642 flags = Location::IsRangeMarker;
3643 _editor->range_bar_drag_rect->hide();
3645 newloc = new Location (
3646 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3649 _editor->session()->locations()->add (newloc, true);
3650 XMLNode &after = _editor->session()->locations()->get_state();
3651 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3652 _editor->commit_reversible_command ();
3656 case CreateTransportMarker:
3657 // popup menu to pick loop or punch
3658 _editor->new_transport_marker_context_menu (&event->button, _item);
3662 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3664 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3669 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3671 if (end == max_framepos) {
3672 end = _editor->session()->current_end_frame ();
3675 if (start == max_framepos) {
3676 start = _editor->session()->current_start_frame ();
3679 switch (_editor->mouse_mode) {
3681 /* find the two markers on either side and then make the selection from it */
3682 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3686 /* find the two markers on either side of the click and make the range out of it */
3687 _editor->selection->set (start, end);
3696 _editor->stop_canvas_autoscroll ();
3700 RangeMarkerBarDrag::aborted (bool)
3706 RangeMarkerBarDrag::update_item (Location* location)
3708 double const x1 = _editor->frame_to_pixel (location->start());
3709 double const x2 = _editor->frame_to_pixel (location->end());
3711 _drag_rect->property_x1() = x1;
3712 _drag_rect->property_x2() = x2;
3715 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3719 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3723 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3725 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3726 Drag::start_grab (event, _editor->cursors()->zoom_out);
3729 Drag::start_grab (event, _editor->cursors()->zoom_in);
3733 show_verbose_cursor_time (adjusted_current_frame (event));
3737 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3742 framepos_t const pf = adjusted_current_frame (event);
3744 framepos_t grab = grab_frame ();
3745 _editor->snap_to_with_modifier (grab, event);
3747 /* base start and end on initial click position */
3759 _editor->zoom_rect->show();
3760 _editor->zoom_rect->raise_to_top();
3763 _editor->reposition_zoom_rect(start, end);
3765 show_verbose_cursor_time (pf);
3770 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3772 if (movement_occurred) {
3773 motion (event, false);
3775 if (grab_frame() < last_pointer_frame()) {
3776 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3778 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3781 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3782 _editor->tav_zoom_step (_zoom_out);
3784 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3788 _editor->zoom_rect->hide();
3792 MouseZoomDrag::aborted (bool)
3794 _editor->zoom_rect->hide ();
3797 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3799 , _cumulative_dx (0)
3800 , _cumulative_dy (0)
3802 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3804 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3805 _region = &_primary->region_view ();
3806 _note_height = _region->midi_stream_view()->note_height ();
3810 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3812 Drag::start_grab (event);
3814 if (!(_was_selected = _primary->selected())) {
3816 /* tertiary-click means extend selection - we'll do that on button release,
3817 so don't add it here, because otherwise we make it hard to figure
3818 out the "extend-to" range.
3821 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3824 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3827 _region->note_selected (_primary, true);
3829 _region->unique_select (_primary);
3835 /** @return Current total drag x change in frames */
3837 NoteDrag::total_dx () const
3840 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3842 /* primary note time */
3843 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3845 /* new time of the primary note in session frames */
3846 frameoffset_t st = n + dx;
3848 framepos_t const rp = _region->region()->position ();
3850 /* prevent the note being dragged earlier than the region's position */
3853 /* snap and return corresponding delta */
3854 return _region->snap_frame_to_frame (st - rp) + rp - n;
3857 /** @return Current total drag y change in note number */
3859 NoteDrag::total_dy () const
3861 return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
3865 NoteDrag::motion (GdkEvent *, bool)
3867 /* Total change in x and y since the start of the drag */
3868 frameoffset_t const dx = total_dx ();
3869 int8_t const dy = total_dy ();
3871 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3872 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3873 double const tdy = -dy * _note_height - _cumulative_dy;
3876 _cumulative_dx += tdx;
3877 _cumulative_dy += tdy;
3879 int8_t note_delta = total_dy();
3881 _region->move_selection (tdx, tdy, note_delta);
3883 /* the new note value may be the same as the old one, but we
3884 * don't know what that means because the selection may have
3885 * involved more than one note and we might be doing something
3886 * odd with them. so show the note value anyway, always.
3890 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
3892 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
3893 (int) floor (new_note));
3895 show_verbose_cursor_text (buf);
3900 NoteDrag::finished (GdkEvent* ev, bool moved)
3903 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3905 if (_was_selected) {
3906 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3908 _region->note_deselected (_primary);
3911 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3912 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3914 if (!extend && !add && _region->selection_size() > 1) {
3915 _region->unique_select (_primary);
3916 } else if (extend) {
3917 _region->note_selected (_primary, true, true);
3919 /* it was added during button press */
3924 _region->note_dropped (_primary, total_dx(), total_dy());
3929 NoteDrag::aborted (bool)
3934 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3935 : Drag (editor, item)
3937 , _nothing_to_drag (false)
3939 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3941 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3944 /* get all lines in the automation view */
3945 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3947 /* find those that overlap the ranges being dragged */
3948 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3949 while (i != lines.end ()) {
3950 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3953 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3955 /* check this range against all the AudioRanges that we are using */
3956 list<AudioRange>::const_iterator k = _ranges.begin ();
3957 while (k != _ranges.end()) {
3958 if (k->coverage (r.first, r.second) != OverlapNone) {
3964 /* add it to our list if it overlaps at all */
3965 if (k != _ranges.end()) {
3970 _lines.push_back (n);
3976 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3980 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3982 Drag::start_grab (event, cursor);
3984 /* Get line states before we start changing things */
3985 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3986 i->state = &i->line->get_state ();
3989 if (_ranges.empty()) {
3991 /* No selected time ranges: drag all points */
3992 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3993 uint32_t const N = i->line->npoints ();
3994 for (uint32_t j = 0; j < N; ++j) {
3995 i->points.push_back (i->line->nth (j));
4001 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4003 framecnt_t const half = (i->start + i->end) / 2;
4005 /* find the line that this audio range starts in */
4006 list<Line>::iterator j = _lines.begin();
4007 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4011 if (j != _lines.end()) {
4012 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4014 /* j is the line that this audio range starts in; fade into it;
4015 64 samples length plucked out of thin air.
4018 framepos_t a = i->start + 64;
4023 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4024 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4026 the_list->add (p, the_list->eval (p));
4027 j->line->add_always_in_view (p);
4028 the_list->add (q, the_list->eval (q));
4029 j->line->add_always_in_view (q);
4032 /* same thing for the end */
4035 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4039 if (j != _lines.end()) {
4040 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4042 /* j is the line that this audio range starts in; fade out of it;
4043 64 samples length plucked out of thin air.
4046 framepos_t b = i->end - 64;
4051 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4052 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4054 the_list->add (p, the_list->eval (p));
4055 j->line->add_always_in_view (p);
4056 the_list->add (q, the_list->eval (q));
4057 j->line->add_always_in_view (q);
4061 _nothing_to_drag = true;
4063 /* Find all the points that should be dragged and put them in the relevant
4064 points lists in the Line structs.
4067 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4069 uint32_t const N = i->line->npoints ();
4070 for (uint32_t j = 0; j < N; ++j) {
4072 /* here's a control point on this line */
4073 ControlPoint* p = i->line->nth (j);
4074 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4076 /* see if it's inside a range */
4077 list<AudioRange>::const_iterator k = _ranges.begin ();
4078 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4082 if (k != _ranges.end()) {
4083 /* dragging this point */
4084 _nothing_to_drag = false;
4085 i->points.push_back (p);
4091 if (_nothing_to_drag) {
4095 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4096 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4101 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4103 if (_nothing_to_drag) {
4107 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4108 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4110 /* we are ignoring x position for this drag, so we can just pass in anything */
4111 i->line->drag_motion (0, f, true, false);
4116 AutomationRangeDrag::finished (GdkEvent* event, bool)
4118 if (_nothing_to_drag) {
4122 motion (event, false);
4123 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4124 i->line->end_drag ();
4125 i->line->clear_always_in_view ();
4128 _editor->session()->commit_reversible_command ();
4132 AutomationRangeDrag::aborted (bool)
4134 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4135 i->line->clear_always_in_view ();
4140 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4143 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4144 layer = v->region()->layer ();
4145 initial_y = v->get_canvas_group()->property_y ();
4146 initial_playlist = v->region()->playlist ();
4147 initial_position = v->region()->position ();
4148 initial_end = v->region()->position () + v->region()->length ();
4151 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4155 , _cumulative_dx (0)
4157 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4161 PatchChangeDrag::motion (GdkEvent* ev, bool)
4163 framepos_t f = adjusted_current_frame (ev);
4164 boost::shared_ptr<Region> r = _region_view->region ();
4165 f = max (f, r->position ());
4166 f = min (f, r->last_frame ());
4168 framecnt_t const dxf = f - grab_frame();
4169 double const dxu = _editor->frame_to_unit (dxf);
4170 _patch_change->move (dxu - _cumulative_dx, 0);
4171 _cumulative_dx = dxu;
4175 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4177 if (!movement_occurred) {
4181 boost::shared_ptr<Region> r (_region_view->region ());
4183 framepos_t f = adjusted_current_frame (ev);
4184 f = max (f, r->position ());
4185 f = min (f, r->last_frame ());
4187 _region_view->move_patch_change (
4189 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4194 PatchChangeDrag::aborted (bool)
4196 _patch_change->move (-_cumulative_dx, 0);
4200 PatchChangeDrag::setup_pointer_frame_offset ()
4202 boost::shared_ptr<Region> region = _region_view->region ();
4203 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4206 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4207 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4214 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4216 framepos_t const p = _region_view->region()->position ();
4217 double const y = _region_view->midi_view()->y_position ();
4219 x1 = max ((framepos_t) 0, x1 - p);
4220 x2 = max ((framepos_t) 0, x2 - p);
4221 y1 = max (0.0, y1 - y);
4222 y2 = max (0.0, y2 - y);
4224 _region_view->update_drag_selection (
4225 _editor->frame_to_pixel (x1),
4226 _editor->frame_to_pixel (x2),
4229 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4234 MidiRubberbandSelectDrag::deselect_things ()
4239 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4240 : RubberbandSelectDrag (e, i)
4246 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4248 if (drag_in_progress) {
4249 /* We just want to select things at the end of the drag, not during it */
4253 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4255 _editor->begin_reversible_command (_("rubberband selection"));
4256 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4257 _editor->commit_reversible_command ();
4261 EditorRubberbandSelectDrag::deselect_things ()
4263 if (!getenv("ARDOUR_SAE")) {
4264 _editor->selection->clear_tracks();
4266 _editor->selection->clear_regions();
4267 _editor->selection->clear_points ();
4268 _editor->selection->clear_lines ();
4271 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4279 NoteCreateDrag::~NoteCreateDrag ()
4285 NoteCreateDrag::grid_frames (framepos_t t) const
4288 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4293 return _region_view->region_beats_to_region_frames (grid_beats);
4297 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4299 Drag::start_grab (event, cursor);
4301 _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4303 framepos_t pf = _drags->current_pointer_frame ();
4304 framecnt_t const g = grid_frames (pf);
4306 /* Hack so that we always snap to the note that we are over, instead of snapping
4307 to the next one if we're more than halfway through the one we're over.
4309 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4313 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4315 MidiStreamView* sv = _region_view->midi_stream_view ();
4316 double const x = _editor->frame_to_pixel (_note[0]);
4317 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4319 _drag_rect->property_x1() = x;
4320 _drag_rect->property_y1() = y;
4321 _drag_rect->property_x2() = x;
4322 _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4324 _drag_rect->property_outline_what() = 0xff;
4325 _drag_rect->property_outline_color_rgba() = 0xffffff99;
4326 _drag_rect->property_fill_color_rgba() = 0xffffff66;
4330 NoteCreateDrag::motion (GdkEvent* event, bool)
4332 _note[1] = adjusted_current_frame (event) - _region_view->region()->position ();
4333 double const x = _editor->frame_to_pixel (_note[1]);
4334 if (_note[1] > _note[0]) {
4335 _drag_rect->property_x2() = x;
4337 _drag_rect->property_x1() = x;
4342 NoteCreateDrag::finished (GdkEvent* event, bool had_movement)
4344 if (!had_movement) {
4348 framepos_t const start = min (_note[0], _note[1]);
4349 framecnt_t length = abs (_note[0] - _note[1]);
4351 framecnt_t const g = grid_frames (start);
4352 if (_editor->snap_mode() == SnapNormal && length < g) {
4356 _region_view->create_note_at (start, _drag_rect->property_y1(), _region_view->region_frames_to_region_beats (length), true, false);
4360 NoteCreateDrag::y_to_region (double y) const
4363 _region_view->get_canvas_group()->w2i (x, y);
4368 NoteCreateDrag::aborted (bool)