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"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/scroll_group.h"
48 #include "audio_region_view.h"
49 #include "midi_region_view.h"
50 #include "ardour_ui.h"
51 #include "gui_thread.h"
52 #include "control_point.h"
54 #include "region_gain_line.h"
55 #include "editor_drag.h"
56 #include "audio_time_axis.h"
57 #include "midi_time_axis.h"
58 #include "selection.h"
59 #include "midi_selection.h"
60 #include "automation_time_axis.h"
62 #include "editor_cursors.h"
63 #include "mouse_cursors.h"
64 #include "note_base.h"
65 #include "patch_change.h"
66 #include "verbose_cursor.h"
69 using namespace ARDOUR;
72 using namespace Gtkmm2ext;
73 using namespace Editing;
74 using namespace ArdourCanvas;
76 using Gtkmm2ext::Keyboard;
78 double ControlPointDrag::_zero_gain_fraction = -1.0;
80 DragManager::DragManager (Editor* e)
83 , _current_pointer_frame (0)
87 DragManager::~DragManager ()
92 /** Call abort for each active drag */
98 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
103 if (!_drags.empty ()) {
104 _editor->set_follow_playhead (_old_follow_playhead, false);
113 DragManager::add (Drag* d)
115 d->set_manager (this);
116 _drags.push_back (d);
120 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
122 d->set_manager (this);
123 _drags.push_back (d);
128 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
130 /* Prevent follow playhead during the drag to be nice to the user */
131 _old_follow_playhead = _editor->follow_playhead ();
132 _editor->set_follow_playhead (false);
134 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
136 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
137 (*i)->start_grab (e, c);
141 /** Call end_grab for each active drag.
142 * @return true if any drag reported movement having occurred.
145 DragManager::end_grab (GdkEvent* e)
150 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
151 bool const t = (*i)->end_grab (e);
162 _editor->set_follow_playhead (_old_follow_playhead, false);
168 DragManager::mark_double_click ()
170 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
171 (*i)->set_double_click (true);
176 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
180 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
182 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
183 bool const t = (*i)->motion_handler (e, from_autoscroll);
184 /* run all handlers; return true if at least one of them
185 returns true (indicating that the event has been handled).
197 DragManager::window_motion_handler (GdkEvent* e, bool from_autoscroll)
201 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
203 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
204 bool const t = (*i)->motion_handler (e, from_autoscroll);
215 DragManager::have_item (ArdourCanvas::Item* i) const
217 list<Drag*>::const_iterator j = _drags.begin ();
218 while (j != _drags.end() && (*j)->item () != i) {
222 return j != _drags.end ();
225 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
228 , _pointer_frame_offset (0)
229 , _trackview_only (trackview_only)
230 , _move_threshold_passed (false)
231 , _was_double_click (false)
232 , _raw_grab_frame (0)
234 , _last_pointer_frame (0)
240 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
253 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
255 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
257 if (Keyboard::is_button2_event (&event->button)) {
258 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
259 _y_constrained = true;
260 _x_constrained = false;
262 _y_constrained = false;
263 _x_constrained = true;
266 _x_constrained = false;
267 _y_constrained = false;
270 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
271 setup_pointer_frame_offset ();
272 _grab_frame = adjusted_frame (_raw_grab_frame, event);
273 _last_pointer_frame = _grab_frame;
274 _last_pointer_x = _grab_x;
276 if (_trackview_only) {
277 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
280 _last_pointer_y = _grab_y;
286 /* CAIROCANVAS need a variant here that passes *cursor */
291 if (_editor->session() && _editor->session()->transport_rolling()) {
294 _was_rolling = false;
297 switch (_editor->snap_type()) {
298 case SnapToRegionStart:
299 case SnapToRegionEnd:
300 case SnapToRegionSync:
301 case SnapToRegionBoundary:
302 _editor->build_region_boundary_cache ();
309 /** Call to end a drag `successfully'. Ungrabs item and calls
310 * subclass' finished() method.
312 * @param event GDK event, or 0.
313 * @return true if some movement occurred, otherwise false.
316 Drag::end_grab (GdkEvent* event)
318 _editor->stop_canvas_autoscroll ();
322 finished (event, _move_threshold_passed);
324 _editor->verbose_cursor()->hide ();
326 return _move_threshold_passed;
330 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
334 if (f > _pointer_frame_offset) {
335 pos = f - _pointer_frame_offset;
339 _editor->snap_to_with_modifier (pos, event);
346 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
348 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
352 Drag::current_pointer_y () const
354 if (!_trackview_only) {
355 return _drags->current_pointer_y ();
358 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
362 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
364 /* check to see if we have moved in any way that matters since the last motion event */
365 if (_move_threshold_passed &&
366 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
367 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
371 pair<framecnt_t, int> const threshold = move_threshold ();
373 bool const old_move_threshold_passed = _move_threshold_passed;
375 if (!from_autoscroll && !_move_threshold_passed) {
377 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
378 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
380 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
383 if (active (_editor->mouse_mode) && _move_threshold_passed) {
385 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
386 if (!from_autoscroll) {
387 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
390 if (!_editor->autoscroll_active() || from_autoscroll) {
391 motion (event, _move_threshold_passed != old_move_threshold_passed);
393 _last_pointer_x = _drags->current_pointer_x ();
394 _last_pointer_y = current_pointer_y ();
395 _last_pointer_frame = adjusted_current_frame (event);
404 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
412 aborted (_move_threshold_passed);
414 _editor->stop_canvas_autoscroll ();
415 _editor->verbose_cursor()->hide ();
419 Drag::show_verbose_cursor_time (framepos_t frame)
421 /* We use DragManager::current_pointer_y() here
422 because we need to position the verbose canvas
423 cursor within the overall canvas, regardless
424 of this particular drag's _trackview_only
428 _editor->verbose_cursor()->set_time (
430 _drags->current_pointer_x() + 10,
431 _drags->current_pointer_y() + 10
434 _editor->verbose_cursor()->show ();
438 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
440 _editor->verbose_cursor()->show (xoffset);
442 /* We use DragManager::current_pointer_y() here
443 because we need to position the verbose canvas
444 cursor within the overall canvas, regardless
445 of this particular drag's _trackview_only
449 _editor->verbose_cursor()->set_duration (
451 _drags->current_pointer_x() + 10,
452 _drags->current_pointer_y() + 10
457 Drag::show_verbose_cursor_text (string const & text)
459 _editor->verbose_cursor()->show ();
461 /* We use DragManager::current_pointer_y() here
462 because we need to position the verbose canvas
463 cursor within the overall canvas, regardless
464 of this particular drag's _trackview_only
468 _editor->verbose_cursor()->set (
470 _drags->current_pointer_x() + 10,
471 _drags->current_pointer_y() + 10
475 boost::shared_ptr<Region>
476 Drag::add_midi_region (MidiTimeAxisView* view)
478 if (_editor->session()) {
479 const TempoMap& map (_editor->session()->tempo_map());
480 framecnt_t pos = grab_frame();
481 const Meter& m = map.meter_at (pos);
482 /* not that the frame rate used here can be affected by pull up/down which
485 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
486 return view->add_region (grab_frame(), len, true);
489 return boost::shared_ptr<Region>();
492 struct EditorOrderTimeAxisViewSorter {
493 bool operator() (TimeAxisView* a, TimeAxisView* b) {
494 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
495 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
497 return ra->route()->order_key () < rb->route()->order_key ();
501 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
505 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
507 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
508 as some of the regions we are dragging may be on such tracks.
511 TrackViewList track_views = _editor->track_views;
512 track_views.sort (EditorOrderTimeAxisViewSorter ());
514 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
515 _time_axis_views.push_back (*i);
517 TimeAxisView::Children children_list = (*i)->get_child_list ();
518 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
519 _time_axis_views.push_back (j->get());
523 /* the list of views can be empty at this point if this is a region list-insert drag
526 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
527 _views.push_back (DraggingView (*i, this));
530 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
534 RegionDrag::region_going_away (RegionView* v)
536 list<DraggingView>::iterator i = _views.begin ();
537 while (i != _views.end() && i->view != v) {
541 if (i != _views.end()) {
546 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
547 * or -1 if it is not found.
550 RegionDrag::find_time_axis_view (TimeAxisView* t) const
553 int const N = _time_axis_views.size ();
554 while (i < N && _time_axis_views[i] != t) {
565 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
566 : RegionDrag (e, i, p, v)
569 , _last_pointer_time_axis_view (0)
570 , _last_pointer_layer (0)
572 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
576 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
578 Drag::start_grab (event, cursor);
580 show_verbose_cursor_time (_last_frame_position);
582 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
584 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
585 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
590 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
592 /* compute the amount of pointer motion in frames, and where
593 the region would be if we moved it by that much.
595 *pending_region_position = adjusted_current_frame (event);
597 framepos_t sync_frame;
598 framecnt_t sync_offset;
601 sync_offset = _primary->region()->sync_offset (sync_dir);
603 /* we don't handle a sync point that lies before zero.
605 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
607 sync_frame = *pending_region_position + (sync_dir*sync_offset);
609 _editor->snap_to_with_modifier (sync_frame, event);
611 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
614 *pending_region_position = _last_frame_position;
617 if (*pending_region_position > max_framepos - _primary->region()->length()) {
618 *pending_region_position = _last_frame_position;
623 /* in locked edit mode, reverse the usual meaning of _x_constrained */
624 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
626 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
628 /* x movement since last time (in pixels) */
629 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
631 /* total x movement */
632 framecnt_t total_dx = *pending_region_position;
633 if (regions_came_from_canvas()) {
634 total_dx = total_dx - grab_frame ();
637 /* check that no regions have gone off the start of the session */
638 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
639 if ((i->view->region()->position() + total_dx) < 0) {
641 *pending_region_position = _last_frame_position;
646 _last_frame_position = *pending_region_position;
653 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
655 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
656 int const n = i->time_axis_view + delta_track;
657 if (n < 0 || n >= int (_time_axis_views.size())) {
658 /* off the top or bottom track */
662 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
663 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
664 /* not a track, or the wrong type */
668 double const l = i->layer + delta_layer;
670 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
671 mode to allow the user to place a region below another on layer 0.
673 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
674 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
675 If it has, the layers will be munged later anyway, so it's ok.
681 /* all regions being dragged are ok with this change */
686 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
688 double delta_layer = 0;
689 int delta_time_axis_view = 0;
691 assert (!_views.empty ());
693 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
695 /* Find the TimeAxisView that the pointer is now over */
696 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
697 TimeAxisView* tv = r.first;
699 if (tv && tv->view()) {
700 double layer = r.second;
702 if (first_move && tv->view()->layer_display() == Stacked) {
703 tv->view()->set_layer_display (Expanded);
706 /* Here's the current pointer position in terms of time axis view and layer */
707 int const current_pointer_time_axis_view = find_time_axis_view (tv);
708 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
710 /* Work out the change in y */
712 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
713 delta_layer = current_pointer_layer - _last_pointer_layer;
716 /* Work out the change in x */
717 framepos_t pending_region_position;
718 double const x_delta = compute_x_delta (event, &pending_region_position);
720 /* Verify change in y */
721 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
722 /* this y movement is not allowed, so do no y movement this time */
723 delta_time_axis_view = 0;
727 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
728 /* haven't reached next snap point, and we're not switching
729 trackviews nor layers. nothing to do.
734 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
736 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
738 RegionView* rv = i->view;
740 if (rv->region()->locked() || rv->region()->video_locked()) {
746 rv->fake_set_opaque (true);
748 /* reparent the regionview into a group above all
752 ArdourCanvas::Group* rvg = rv->get_canvas_group();
753 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
754 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
755 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
756 /* move the item so that it continues to appear at the
757 same location now that its parent has changed.
759 rvg->move (rv_canvas_offset - dmg_canvas_offset);
762 /* If we have moved tracks, we'll fudge the layer delta so that the
763 region gets moved back onto layer 0 on its new track; this avoids
764 confusion when dragging regions from non-zero layers onto different
767 double this_delta_layer = delta_layer;
768 if (delta_time_axis_view != 0) {
769 this_delta_layer = - i->layer;
776 if (i->time_axis_view >= 0) {
777 track_index = i->time_axis_view + delta_time_axis_view;
779 track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
782 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
786 /* The TimeAxisView that this region is now over */
787 TimeAxisView* current_tv = _time_axis_views[track_index];
789 /* Ensure it is moved from stacked -> expanded if appropriate */
790 if (current_tv->view()->layer_display() == Stacked) {
791 current_tv->view()->set_layer_display (Expanded);
794 /* We're only allowed to go -ve in layer on Expanded views */
795 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
796 this_delta_layer = - i->layer;
800 rv->set_height (current_tv->view()->child_height ());
802 /* Update show/hidden status as the region view may have come from a hidden track,
803 or have moved to one.
805 if (current_tv->hidden ()) {
806 rv->get_canvas_group()->hide ();
808 rv->get_canvas_group()->show ();
811 /* Update the DraggingView */
812 i->time_axis_view = track_index;
813 i->layer += this_delta_layer;
816 _editor->mouse_brush_insert_region (rv, pending_region_position);
820 /* Get the y coordinate of the top of the track that this region is now over */
821 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
823 /* And adjust for the layer that it should be on */
824 StreamView* cv = current_tv->view ();
825 switch (cv->layer_display ()) {
829 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
832 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
836 /* need to get the parent of the regionview
837 * canvas group and get its position in
838 * equivalent coordinate space as the trackview
839 * we are now dragging over.
842 /* Now move the region view */
843 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->canvas_origin().y);
847 /* Only move the region into the empty dropzone at the bottom if the pointer
851 if (current_pointer_y() >= 0) {
853 Coord last_track_bottom_edge;
854 if (!_time_axis_views.empty()) {
855 TimeAxisView* last = _time_axis_views.back();
856 last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
858 last_track_bottom_edge = 0;
861 rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y);
862 i->time_axis_view = -1;
866 } /* foreach region */
868 _total_x_delta += x_delta;
870 if (x_delta != 0 && !_brushing) {
871 show_verbose_cursor_time (_last_frame_position);
874 _last_pointer_time_axis_view += delta_time_axis_view;
875 _last_pointer_layer += delta_layer;
879 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
881 if (_copy && first_move) {
883 /* duplicate the regionview(s) and region(s) */
885 list<DraggingView> new_regionviews;
887 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
889 RegionView* rv = i->view;
890 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
891 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
893 const boost::shared_ptr<const Region> original = rv->region();
894 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
895 region_copy->set_position (original->position());
899 boost::shared_ptr<AudioRegion> audioregion_copy
900 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
902 nrv = new AudioRegionView (*arv, audioregion_copy);
904 boost::shared_ptr<MidiRegion> midiregion_copy
905 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
906 nrv = new MidiRegionView (*mrv, midiregion_copy);
911 nrv->get_canvas_group()->show ();
912 new_regionviews.push_back (DraggingView (nrv, this));
914 /* swap _primary to the copy */
916 if (rv == _primary) {
920 /* ..and deselect the one we copied */
922 rv->set_selected (false);
925 if (!new_regionviews.empty()) {
927 /* reflect the fact that we are dragging the copies */
929 _views = new_regionviews;
931 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
935 RegionMotionDrag::motion (event, first_move);
939 RegionMotionDrag::finished (GdkEvent *, bool)
941 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
946 if ((*i)->view()->layer_display() == Expanded) {
947 (*i)->view()->set_layer_display (Stacked);
953 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
955 RegionMotionDrag::finished (ev, movement_occurred);
957 if (!movement_occurred) {
961 if (was_double_click() && !_views.empty()) {
962 DraggingView dv = _views.front();
963 dv.view->show_region_editor ();
970 /* reverse this here so that we have the correct logic to finalize
974 if (Config->get_edit_mode() == Lock) {
975 _x_constrained = !_x_constrained;
978 assert (!_views.empty ());
980 /* We might have hidden region views so that they weren't visible during the drag
981 (when they have been reparented). Now everything can be shown again, as region
982 views are back in their track parent groups.
984 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
985 i->view->get_canvas_group()->show ();
988 bool const changed_position = (_last_frame_position != _primary->region()->position());
989 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
990 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1010 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1014 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region)
1016 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1021 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1022 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1023 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1024 return _editor->axis_view_from_route (audio_tracks.front());
1026 ChanCount one_midi_port (DataType::MIDI, 1);
1027 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1028 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1029 return _editor->axis_view_from_route (midi_tracks.front());
1032 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1038 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1040 RegionSelection new_views;
1041 PlaylistSet modified_playlists;
1042 RouteTimeAxisView* new_time_axis_view = 0;
1045 /* all changes were made during motion event handlers */
1047 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1051 _editor->commit_reversible_command ();
1055 if (_x_constrained) {
1056 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1058 _editor->begin_reversible_command (Operations::region_copy);
1061 /* insert the regions into their new playlists */
1062 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1064 RouteTimeAxisView* dest_rtv = 0;
1066 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1072 if (changed_position && !_x_constrained) {
1073 where = i->view->region()->position() - drag_delta;
1075 where = i->view->region()->position();
1078 if (i->time_axis_view < 0) {
1079 if (!new_time_axis_view) {
1080 new_time_axis_view = create_destination_time_axis (i->view->region());
1082 dest_rtv = new_time_axis_view;
1084 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1087 if (dest_rtv != 0) {
1088 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1089 if (new_view != 0) {
1090 new_views.push_back (new_view);
1094 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1095 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1098 list<DraggingView>::const_iterator next = i;
1104 /* If we've created new regions either by copying or moving
1105 to a new track, we want to replace the old selection with the new ones
1108 if (new_views.size() > 0) {
1109 _editor->selection->set (new_views);
1112 /* write commands for the accumulated diffs for all our modified playlists */
1113 add_stateful_diff_commands_for_playlists (modified_playlists);
1115 _editor->commit_reversible_command ();
1119 RegionMoveDrag::finished_no_copy (
1120 bool const changed_position,
1121 bool const changed_tracks,
1122 framecnt_t const drag_delta
1125 RegionSelection new_views;
1126 PlaylistSet modified_playlists;
1127 PlaylistSet frozen_playlists;
1128 set<RouteTimeAxisView*> views_to_update;
1129 RouteTimeAxisView* new_time_axis_view = 0;
1132 /* all changes were made during motion event handlers */
1133 _editor->commit_reversible_command ();
1137 if (_x_constrained) {
1138 _editor->begin_reversible_command (_("fixed time region drag"));
1140 _editor->begin_reversible_command (Operations::region_drag);
1143 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1145 RegionView* rv = i->view;
1146 RouteTimeAxisView* dest_rtv = 0;
1148 if (rv->region()->locked() || rv->region()->video_locked()) {
1153 if (i->time_axis_view < 0) {
1154 if (!new_time_axis_view) {
1155 new_time_axis_view = create_destination_time_axis (rv->region());
1157 dest_rtv = new_time_axis_view;
1159 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1164 double const dest_layer = i->layer;
1166 views_to_update.insert (dest_rtv);
1170 if (changed_position && !_x_constrained) {
1171 where = rv->region()->position() - drag_delta;
1173 where = rv->region()->position();
1176 if (changed_tracks) {
1178 /* insert into new playlist */
1180 RegionView* new_view = insert_region_into_playlist (
1181 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1184 if (new_view == 0) {
1189 new_views.push_back (new_view);
1191 /* remove from old playlist */
1193 /* the region that used to be in the old playlist is not
1194 moved to the new one - we use a copy of it. as a result,
1195 any existing editor for the region should no longer be
1198 rv->hide_region_editor();
1199 rv->fake_set_opaque (false);
1201 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1205 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1207 /* this movement may result in a crossfade being modified, or a layering change,
1208 so we need to get undo data from the playlist as well as the region.
1211 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1213 playlist->clear_changes ();
1216 rv->region()->clear_changes ();
1219 motion on the same track. plonk the previously reparented region
1220 back to its original canvas group (its streamview).
1221 No need to do anything for copies as they are fake regions which will be deleted.
1224 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1225 rv->get_canvas_group()->set_y_position (i->initial_y);
1228 /* just change the model */
1229 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1230 playlist->set_layer (rv->region(), dest_layer);
1233 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1235 r = frozen_playlists.insert (playlist);
1238 playlist->freeze ();
1241 rv->region()->set_position (where);
1243 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1246 if (changed_tracks) {
1248 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1249 was selected in all of them, then removing it from a playlist will have removed all
1250 trace of it from _views (i.e. there were N regions selected, we removed 1,
1251 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1252 corresponding regionview, and _views is now empty).
1254 This could have invalidated any and all iterators into _views.
1256 The heuristic we use here is: if the region selection is empty, break out of the loop
1257 here. if the region selection is not empty, then restart the loop because we know that
1258 we must have removed at least the region(view) we've just been working on as well as any
1259 that we processed on previous iterations.
1261 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1262 we can just iterate.
1266 if (_views.empty()) {
1277 /* If we've created new regions either by copying or moving
1278 to a new track, we want to replace the old selection with the new ones
1281 if (new_views.size() > 0) {
1282 _editor->selection->set (new_views);
1285 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1289 /* write commands for the accumulated diffs for all our modified playlists */
1290 add_stateful_diff_commands_for_playlists (modified_playlists);
1292 _editor->commit_reversible_command ();
1294 /* We have futzed with the layering of canvas items on our streamviews.
1295 If any region changed layer, this will have resulted in the stream
1296 views being asked to set up their region views, and all will be well.
1297 If not, we might now have badly-ordered region views. Ask the StreamViews
1298 involved to sort themselves out, just in case.
1301 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1302 (*i)->view()->playlist_layered ((*i)->track ());
1306 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1307 * @param region Region to remove.
1308 * @param playlist playlist To remove from.
1309 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1310 * that clear_changes () is only called once per playlist.
1313 RegionMoveDrag::remove_region_from_playlist (
1314 boost::shared_ptr<Region> region,
1315 boost::shared_ptr<Playlist> playlist,
1316 PlaylistSet& modified_playlists
1319 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1322 playlist->clear_changes ();
1325 playlist->remove_region (region);
1329 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1330 * clearing the playlist's diff history first if necessary.
1331 * @param region Region to insert.
1332 * @param dest_rtv Destination RouteTimeAxisView.
1333 * @param dest_layer Destination layer.
1334 * @param where Destination position.
1335 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1336 * that clear_changes () is only called once per playlist.
1337 * @return New RegionView, or 0 if no insert was performed.
1340 RegionMoveDrag::insert_region_into_playlist (
1341 boost::shared_ptr<Region> region,
1342 RouteTimeAxisView* dest_rtv,
1345 PlaylistSet& modified_playlists
1348 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1349 if (!dest_playlist) {
1353 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1354 _new_region_view = 0;
1355 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1357 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1358 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1360 dest_playlist->clear_changes ();
1363 dest_playlist->add_region (region, where);
1365 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1366 dest_playlist->set_layer (region, dest_layer);
1371 assert (_new_region_view);
1373 return _new_region_view;
1377 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1379 _new_region_view = rv;
1383 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1385 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1386 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1388 _editor->session()->add_command (c);
1397 RegionMoveDrag::aborted (bool movement_occurred)
1401 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1408 RegionMotionDrag::aborted (movement_occurred);
1413 RegionMotionDrag::aborted (bool)
1415 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1417 StreamView* sview = (*i)->view();
1420 if (sview->layer_display() == Expanded) {
1421 sview->set_layer_display (Stacked);
1426 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1427 RegionView* rv = i->view;
1428 TimeAxisView* tv = &(rv->get_time_axis_view ());
1429 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1431 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1432 rv->get_canvas_group()->set_y_position (0);
1434 rv->fake_set_opaque (false);
1435 rv->move (-_total_x_delta, 0);
1436 rv->set_height (rtv->view()->child_height ());
1440 /** @param b true to brush, otherwise false.
1441 * @param c true to make copies of the regions being moved, otherwise false.
1443 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1444 : RegionMotionDrag (e, i, p, v, b),
1447 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1450 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1451 if (rtv && rtv->is_track()) {
1452 speed = rtv->track()->speed ();
1455 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1459 RegionMoveDrag::setup_pointer_frame_offset ()
1461 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1464 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1465 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1467 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1469 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1470 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1472 _primary = v->view()->create_region_view (r, false, false);
1474 _primary->get_canvas_group()->show ();
1475 _primary->set_position (pos, 0);
1476 _views.push_back (DraggingView (_primary, this));
1478 _last_frame_position = pos;
1480 _item = _primary->get_canvas_group ();
1484 RegionInsertDrag::finished (GdkEvent *, bool)
1486 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1488 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1489 _primary->get_canvas_group()->set_y_position (0);
1491 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1493 _editor->begin_reversible_command (Operations::insert_region);
1494 playlist->clear_changes ();
1495 playlist->add_region (_primary->region (), _last_frame_position);
1496 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1497 _editor->commit_reversible_command ();
1505 RegionInsertDrag::aborted (bool)
1512 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1513 : RegionMoveDrag (e, i, p, v, false, false)
1515 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1518 struct RegionSelectionByPosition {
1519 bool operator() (RegionView*a, RegionView* b) {
1520 return a->region()->position () < b->region()->position();
1525 RegionSpliceDrag::motion (GdkEvent* event, bool)
1527 /* Which trackview is this ? */
1529 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1530 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1532 /* The region motion is only processed if the pointer is over
1536 if (!tv || !tv->is_track()) {
1537 /* To make sure we hide the verbose canvas cursor when the mouse is
1538 not held over and audiotrack.
1540 _editor->verbose_cursor()->hide ();
1546 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1552 RegionSelection copy (_editor->selection->regions);
1554 RegionSelectionByPosition cmp;
1557 framepos_t const pf = adjusted_current_frame (event);
1559 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1561 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1567 boost::shared_ptr<Playlist> playlist;
1569 if ((playlist = atv->playlist()) == 0) {
1573 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1578 if (pf < (*i)->region()->last_frame() + 1) {
1582 if (pf > (*i)->region()->first_frame()) {
1588 playlist->shuffle ((*i)->region(), dir);
1593 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1595 RegionMoveDrag::finished (event, movement_occurred);
1599 RegionSpliceDrag::aborted (bool)
1604 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1606 _view (dynamic_cast<MidiTimeAxisView*> (v))
1608 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1614 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1617 _region = add_midi_region (_view);
1618 _view->playlist()->freeze ();
1621 framepos_t const f = adjusted_current_frame (event);
1622 if (f < grab_frame()) {
1623 _region->set_position (f);
1626 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1627 so that if this region is duplicated, its duplicate starts on
1628 a snap point rather than 1 frame after a snap point. Otherwise things get
1629 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1630 place snapped notes at the start of the region.
1633 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1634 _region->set_length (len < 1 ? 1 : len);
1640 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1642 if (!movement_occurred) {
1643 add_midi_region (_view);
1645 _view->playlist()->thaw ();
1650 RegionCreateDrag::aborted (bool)
1653 _view->playlist()->thaw ();
1659 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1663 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1667 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1669 Gdk::Cursor* cursor;
1670 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1672 float x_fraction = cnote->mouse_x_fraction ();
1674 if (x_fraction > 0.0 && x_fraction < 0.25) {
1675 cursor = _editor->cursors()->left_side_trim;
1677 cursor = _editor->cursors()->right_side_trim;
1680 Drag::start_grab (event, cursor);
1682 region = &cnote->region_view();
1684 double const region_start = region->get_position_pixels();
1685 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1687 if (grab_x() <= middle_point) {
1688 cursor = _editor->cursors()->left_side_trim;
1691 cursor = _editor->cursors()->right_side_trim;
1697 if (event->motion.state & Keyboard::PrimaryModifier) {
1703 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1705 if (ms.size() > 1) {
1706 /* has to be relative, may make no sense otherwise */
1710 /* select this note; if it is already selected, preserve the existing selection,
1711 otherwise make this note the only one selected.
1713 region->note_selected (cnote, cnote->selected ());
1715 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1716 MidiRegionSelection::iterator next;
1719 (*r)->begin_resizing (at_front);
1725 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1727 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1728 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1729 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1731 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1736 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1738 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1739 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1740 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1742 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1747 NoteResizeDrag::aborted (bool)
1749 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1750 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1751 (*r)->abort_resizing ();
1755 AVDraggingView::AVDraggingView (RegionView* v)
1758 initial_position = v->region()->position ();
1761 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1764 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1767 TrackViewList empty;
1769 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1770 std::list<RegionView*> views = rs.by_layer();
1772 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1773 RegionView* rv = (*i);
1774 if (!rv->region()->video_locked()) {
1777 _views.push_back (AVDraggingView (rv));
1782 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1784 Drag::start_grab (event);
1785 if (_editor->session() == 0) {
1789 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1790 _max_backwards_drag = (
1791 ARDOUR_UI::instance()->video_timeline->get_duration()
1792 + ARDOUR_UI::instance()->video_timeline->get_offset()
1793 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1796 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1797 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1798 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1801 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1804 Timecode::Time timecode;
1805 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1806 snprintf (buf, sizeof (buf), "Video Start:\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, (_startdrag_video_offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1807 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1808 _editor->verbose_cursor()->show ();
1812 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1814 if (_editor->session() == 0) {
1817 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1821 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1822 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
1824 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1825 dt = - _max_backwards_drag;
1828 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1829 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1831 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1832 RegionView* rv = i->view;
1833 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1836 rv->fake_set_opaque (true);
1837 rv->region()->clear_changes ();
1838 rv->region()->suspend_property_changes();
1840 rv->region()->set_position(i->initial_position + dt);
1841 rv->region_changed(ARDOUR::Properties::position);
1844 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1845 Timecode::Time timecode;
1846 Timecode::Time timediff;
1848 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1849 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1850 snprintf (buf, sizeof (buf),
1851 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1852 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1853 , _("Video Start:"),
1854 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1856 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1858 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1859 _editor->verbose_cursor()->show ();
1863 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1865 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1869 if (!movement_occurred || ! _editor->session()) {
1873 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1875 _editor->begin_reversible_command (_("Move Video"));
1877 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1878 ARDOUR_UI::instance()->video_timeline->save_undo();
1879 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1880 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1882 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1883 i->view->drag_end();
1884 i->view->fake_set_opaque (false);
1885 i->view->region()->resume_property_changes ();
1887 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1890 _editor->session()->maybe_update_session_range(
1891 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1892 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1896 _editor->commit_reversible_command ();
1900 VideoTimeLineDrag::aborted (bool)
1902 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1905 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1906 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1908 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1909 i->view->region()->resume_property_changes ();
1910 i->view->region()->set_position(i->initial_position);
1914 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1915 : RegionDrag (e, i, p, v)
1916 , _preserve_fade_anchor (preserve_fade_anchor)
1917 , _jump_position_when_done (false)
1919 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1923 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1926 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1927 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1929 if (tv && tv->is_track()) {
1930 speed = tv->track()->speed();
1933 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1934 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1935 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1937 framepos_t const pf = adjusted_current_frame (event);
1939 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1940 /* Move the contents of the region around without changing the region bounds */
1941 _operation = ContentsTrim;
1942 Drag::start_grab (event, _editor->cursors()->trimmer);
1944 /* These will get overridden for a point trim.*/
1945 if (pf < (region_start + region_length/2)) {
1946 /* closer to front */
1947 _operation = StartTrim;
1948 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1951 _operation = EndTrim;
1952 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1956 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1957 _jump_position_when_done = true;
1960 switch (_operation) {
1962 show_verbose_cursor_time (region_start);
1963 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1964 i->view->trim_front_starting ();
1968 show_verbose_cursor_time (region_end);
1971 show_verbose_cursor_time (pf);
1975 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1976 i->view->region()->suspend_property_changes ();
1981 TrimDrag::motion (GdkEvent* event, bool first_move)
1983 RegionView* rv = _primary;
1986 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1987 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1988 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1989 frameoffset_t frame_delta = 0;
1991 if (tv && tv->is_track()) {
1992 speed = tv->track()->speed();
1995 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2001 switch (_operation) {
2003 trim_type = "Region start trim";
2006 trim_type = "Region end trim";
2009 trim_type = "Region content trim";
2016 _editor->begin_reversible_command (trim_type);
2018 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2019 RegionView* rv = i->view;
2020 rv->fake_set_opaque (false);
2021 rv->enable_display (false);
2022 rv->region()->playlist()->clear_owned_changes ();
2024 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2027 arv->temporarily_hide_envelope ();
2031 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2032 insert_result = _editor->motion_frozen_playlists.insert (pl);
2034 if (insert_result.second) {
2040 bool non_overlap_trim = false;
2042 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2043 non_overlap_trim = true;
2046 /* contstrain trim to fade length */
2047 if (_preserve_fade_anchor) {
2048 switch (_operation) {
2050 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2051 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2053 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2054 if (ar->locked()) continue;
2055 framecnt_t len = ar->fade_in()->back()->when;
2056 if (len < dt) dt = min(dt, len);
2060 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2061 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2063 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2064 if (ar->locked()) continue;
2065 framecnt_t len = ar->fade_out()->back()->when;
2066 if (len < -dt) dt = max(dt, -len);
2075 switch (_operation) {
2077 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2078 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2079 if (changed && _preserve_fade_anchor) {
2080 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2082 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2083 framecnt_t len = ar->fade_in()->back()->when;
2084 framecnt_t diff = ar->first_frame() - i->initial_position;
2085 framepos_t new_length = len - diff;
2086 i->anchored_fade_length = min (ar->length(), new_length);
2087 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2088 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2095 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2096 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2097 if (changed && _preserve_fade_anchor) {
2098 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2100 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2101 framecnt_t len = ar->fade_out()->back()->when;
2102 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2103 framepos_t new_length = len + diff;
2104 i->anchored_fade_length = min (ar->length(), new_length);
2105 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2106 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2114 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2116 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2117 i->view->move_contents (frame_delta);
2123 switch (_operation) {
2125 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2128 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2131 // show_verbose_cursor_time (frame_delta);
2138 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2140 if (movement_occurred) {
2141 motion (event, false);
2143 if (_operation == StartTrim) {
2144 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2146 /* This must happen before the region's StatefulDiffCommand is created, as it may
2147 `correct' (ahem) the region's _start from being negative to being zero. It
2148 needs to be zero in the undo record.
2150 i->view->trim_front_ending ();
2152 if (_preserve_fade_anchor) {
2153 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2155 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2156 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2157 ar->set_fade_in_length(i->anchored_fade_length);
2158 ar->set_fade_in_active(true);
2161 if (_jump_position_when_done) {
2162 i->view->region()->set_position (i->initial_position);
2165 } else if (_operation == EndTrim) {
2166 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2167 if (_preserve_fade_anchor) {
2168 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2170 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2171 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2172 ar->set_fade_out_length(i->anchored_fade_length);
2173 ar->set_fade_out_active(true);
2176 if (_jump_position_when_done) {
2177 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2182 if (!_views.empty()) {
2183 if (_operation == StartTrim) {
2184 _editor->maybe_locate_with_edit_preroll(
2185 _views.begin()->view->region()->position());
2187 if (_operation == EndTrim) {
2188 _editor->maybe_locate_with_edit_preroll(
2189 _views.begin()->view->region()->position() +
2190 _views.begin()->view->region()->length());
2194 if (!_editor->selection->selected (_primary)) {
2195 _primary->thaw_after_trim ();
2198 set<boost::shared_ptr<Playlist> > diffed_playlists;
2200 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2201 i->view->thaw_after_trim ();
2202 i->view->enable_display (true);
2203 i->view->fake_set_opaque (true);
2205 /* Trimming one region may affect others on the playlist, so we need
2206 to get undo Commands from the whole playlist rather than just the
2207 region. Use diffed_playlists to make sure we don't diff a given
2208 playlist more than once.
2210 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2211 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2212 vector<Command*> cmds;
2214 _editor->session()->add_commands (cmds);
2215 diffed_playlists.insert (p);
2220 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2224 _editor->motion_frozen_playlists.clear ();
2225 _editor->commit_reversible_command();
2228 /* no mouse movement */
2229 _editor->point_trim (event, adjusted_current_frame (event));
2232 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2233 if (_operation == StartTrim) {
2234 i->view->trim_front_ending ();
2237 i->view->region()->resume_property_changes ();
2242 TrimDrag::aborted (bool movement_occurred)
2244 /* Our motion method is changing model state, so use the Undo system
2245 to cancel. Perhaps not ideal, as this will leave an Undo point
2246 behind which may be slightly odd from the user's point of view.
2251 if (movement_occurred) {
2255 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2256 i->view->region()->resume_property_changes ();
2261 TrimDrag::setup_pointer_frame_offset ()
2263 list<DraggingView>::iterator i = _views.begin ();
2264 while (i != _views.end() && i->view != _primary) {
2268 if (i == _views.end()) {
2272 switch (_operation) {
2274 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2277 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2284 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2288 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2289 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2294 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2296 Drag::start_grab (event, cursor);
2297 show_verbose_cursor_time (adjusted_current_frame(event));
2301 MeterMarkerDrag::setup_pointer_frame_offset ()
2303 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2307 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2309 if (!_marker->meter().movable()) {
2315 // create a dummy marker for visual representation of moving the
2316 // section, because whether its a copy or not, we're going to
2317 // leave or lose the original marker (leave if its a copy; lose if its
2318 // not, because we'll remove it from the map).
2320 MeterSection section (_marker->meter());
2322 if (!section.movable()) {
2327 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2329 _marker = new MeterMarker (
2331 *_editor->meter_group,
2332 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2334 *new MeterSection (_marker->meter())
2337 /* use the new marker for the grab */
2338 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2341 TempoMap& map (_editor->session()->tempo_map());
2342 /* get current state */
2343 before_state = &map.get_state();
2344 /* remove the section while we drag it */
2345 map.remove_meter (section, true);
2349 framepos_t const pf = adjusted_current_frame (event);
2350 _marker->set_position (pf);
2351 show_verbose_cursor_time (pf);
2355 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2357 if (!movement_occurred) {
2358 if (was_double_click()) {
2359 _editor->edit_meter_marker (*_marker);
2364 if (!_marker->meter().movable()) {
2368 motion (event, false);
2370 Timecode::BBT_Time when;
2372 TempoMap& map (_editor->session()->tempo_map());
2373 map.bbt_time (last_pointer_frame(), when);
2375 if (_copy == true) {
2376 _editor->begin_reversible_command (_("copy meter mark"));
2377 XMLNode &before = map.get_state();
2378 map.add_meter (_marker->meter(), when);
2379 XMLNode &after = map.get_state();
2380 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2381 _editor->commit_reversible_command ();
2384 _editor->begin_reversible_command (_("move meter mark"));
2386 /* we removed it before, so add it back now */
2388 map.add_meter (_marker->meter(), when);
2389 XMLNode &after = map.get_state();
2390 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2391 _editor->commit_reversible_command ();
2394 // delete the dummy marker we used for visual representation while moving.
2395 // a new visual marker will show up automatically.
2400 MeterMarkerDrag::aborted (bool moved)
2402 _marker->set_position (_marker->meter().frame ());
2405 TempoMap& map (_editor->session()->tempo_map());
2406 /* we removed it before, so add it back now */
2407 map.add_meter (_marker->meter(), _marker->meter().frame());
2408 // delete the dummy marker we used for visual representation while moving.
2409 // a new visual marker will show up automatically.
2414 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2418 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2420 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2425 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2427 Drag::start_grab (event, cursor);
2428 show_verbose_cursor_time (adjusted_current_frame (event));
2432 TempoMarkerDrag::setup_pointer_frame_offset ()
2434 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2438 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2440 if (!_marker->tempo().movable()) {
2446 // create a dummy marker for visual representation of moving the
2447 // section, because whether its a copy or not, we're going to
2448 // leave or lose the original marker (leave if its a copy; lose if its
2449 // not, because we'll remove it from the map).
2451 // create a dummy marker for visual representation of moving the copy.
2452 // The actual copying is not done before we reach the finish callback.
2455 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2457 TempoSection section (_marker->tempo());
2459 _marker = new TempoMarker (
2461 *_editor->tempo_group,
2462 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2464 *new TempoSection (_marker->tempo())
2467 /* use the new marker for the grab */
2468 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2471 TempoMap& map (_editor->session()->tempo_map());
2472 /* get current state */
2473 before_state = &map.get_state();
2474 /* remove the section while we drag it */
2475 map.remove_tempo (section, true);
2479 framepos_t const pf = adjusted_current_frame (event);
2480 _marker->set_position (pf);
2481 show_verbose_cursor_time (pf);
2485 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2487 if (!movement_occurred) {
2488 if (was_double_click()) {
2489 _editor->edit_tempo_marker (*_marker);
2494 if (!_marker->tempo().movable()) {
2498 motion (event, false);
2500 TempoMap& map (_editor->session()->tempo_map());
2501 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2502 Timecode::BBT_Time when;
2504 map.bbt_time (beat_time, when);
2506 if (_copy == true) {
2507 _editor->begin_reversible_command (_("copy tempo mark"));
2508 XMLNode &before = map.get_state();
2509 map.add_tempo (_marker->tempo(), when);
2510 XMLNode &after = map.get_state();
2511 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2512 _editor->commit_reversible_command ();
2515 _editor->begin_reversible_command (_("move tempo mark"));
2516 /* we removed it before, so add it back now */
2517 map.add_tempo (_marker->tempo(), when);
2518 XMLNode &after = map.get_state();
2519 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2520 _editor->commit_reversible_command ();
2523 // delete the dummy marker we used for visual representation while moving.
2524 // a new visual marker will show up automatically.
2529 TempoMarkerDrag::aborted (bool moved)
2531 _marker->set_position (_marker->tempo().frame());
2533 TempoMap& map (_editor->session()->tempo_map());
2534 /* we removed it before, so add it back now */
2535 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2536 // delete the dummy marker we used for visual representation while moving.
2537 // a new visual marker will show up automatically.
2542 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2543 : Drag (e, &c.track_canvas_item(), false)
2547 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2550 /** Do all the things we do when dragging the playhead to make it look as though
2551 * we have located, without actually doing the locate (because that would cause
2552 * the diskstream buffers to be refilled, which is too slow).
2555 CursorDrag::fake_locate (framepos_t t)
2557 _editor->playhead_cursor->set_position (t);
2559 Session* s = _editor->session ();
2560 if (s->timecode_transmission_suspended ()) {
2561 framepos_t const f = _editor->playhead_cursor->current_frame ();
2562 /* This is asynchronous so it will be sent "now"
2564 s->send_mmc_locate (f);
2565 /* These are synchronous and will be sent during the next
2568 s->queue_full_time_code ();
2569 s->queue_song_position_pointer ();
2572 show_verbose_cursor_time (t);
2573 _editor->UpdateAllTransportClocks (t);
2577 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2579 Drag::start_grab (event, c);
2581 _grab_zoom = _editor->samples_per_pixel;
2583 framepos_t where = _editor->canvas_event_sample (event);
2585 _editor->snap_to_with_modifier (where, event);
2587 _editor->_dragging_playhead = true;
2589 Session* s = _editor->session ();
2591 /* grab the track canvas item as well */
2593 _cursor.track_canvas_item().grab();
2596 if (_was_rolling && _stop) {
2600 if (s->is_auditioning()) {
2601 s->cancel_audition ();
2605 if (AudioEngine::instance()->connected()) {
2607 /* do this only if we're the engine is connected
2608 * because otherwise this request will never be
2609 * serviced and we'll busy wait forever. likewise,
2610 * notice if we are disconnected while waiting for the
2611 * request to be serviced.
2614 s->request_suspend_timecode_transmission ();
2615 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2616 /* twiddle our thumbs */
2621 fake_locate (where);
2625 CursorDrag::motion (GdkEvent* event, bool)
2627 framepos_t const adjusted_frame = adjusted_current_frame (event);
2628 if (adjusted_frame != last_pointer_frame()) {
2629 fake_locate (adjusted_frame);
2634 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2636 _editor->_dragging_playhead = false;
2638 _cursor.track_canvas_item().ungrab();
2640 if (!movement_occurred && _stop) {
2644 motion (event, false);
2646 Session* s = _editor->session ();
2648 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2649 _editor->_pending_locate_request = true;
2650 s->request_resume_timecode_transmission ();
2655 CursorDrag::aborted (bool)
2657 _cursor.track_canvas_item().ungrab();
2659 if (_editor->_dragging_playhead) {
2660 _editor->session()->request_resume_timecode_transmission ();
2661 _editor->_dragging_playhead = false;
2664 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2667 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2668 : RegionDrag (e, i, p, v)
2670 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2674 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2676 Drag::start_grab (event, cursor);
2678 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2679 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2681 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2685 FadeInDrag::setup_pointer_frame_offset ()
2687 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2688 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2689 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2693 FadeInDrag::motion (GdkEvent* event, bool)
2695 framecnt_t fade_length;
2697 framepos_t const pos = adjusted_current_frame (event);
2699 boost::shared_ptr<Region> region = _primary->region ();
2701 if (pos < (region->position() + 64)) {
2702 fade_length = 64; // this should be a minimum defined somewhere
2703 } else if (pos > region->last_frame()) {
2704 fade_length = region->length();
2706 fade_length = pos - region->position();
2709 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2711 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2717 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2720 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2724 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2726 if (!movement_occurred) {
2730 framecnt_t fade_length;
2732 framepos_t const pos = adjusted_current_frame (event);
2734 boost::shared_ptr<Region> region = _primary->region ();
2736 if (pos < (region->position() + 64)) {
2737 fade_length = 64; // this should be a minimum defined somewhere
2738 } else if (pos > region->last_frame()) {
2739 fade_length = region->length();
2741 fade_length = pos - region->position();
2744 _editor->begin_reversible_command (_("change fade in length"));
2746 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2748 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2754 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2755 XMLNode &before = alist->get_state();
2757 tmp->audio_region()->set_fade_in_length (fade_length);
2758 tmp->audio_region()->set_fade_in_active (true);
2760 XMLNode &after = alist->get_state();
2761 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2764 _editor->commit_reversible_command ();
2768 FadeInDrag::aborted (bool)
2770 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2771 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2777 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2781 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2782 : RegionDrag (e, i, p, v)
2784 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2788 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2790 Drag::start_grab (event, cursor);
2792 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2793 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2795 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2799 FadeOutDrag::setup_pointer_frame_offset ()
2801 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2802 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2803 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2807 FadeOutDrag::motion (GdkEvent* event, bool)
2809 framecnt_t fade_length;
2811 framepos_t const pos = adjusted_current_frame (event);
2813 boost::shared_ptr<Region> region = _primary->region ();
2815 if (pos > (region->last_frame() - 64)) {
2816 fade_length = 64; // this should really be a minimum fade defined somewhere
2818 else if (pos < region->position()) {
2819 fade_length = region->length();
2822 fade_length = region->last_frame() - pos;
2825 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2827 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2833 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2836 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2840 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2842 if (!movement_occurred) {
2846 framecnt_t fade_length;
2848 framepos_t const pos = adjusted_current_frame (event);
2850 boost::shared_ptr<Region> region = _primary->region ();
2852 if (pos > (region->last_frame() - 64)) {
2853 fade_length = 64; // this should really be a minimum fade defined somewhere
2855 else if (pos < region->position()) {
2856 fade_length = region->length();
2859 fade_length = region->last_frame() - pos;
2862 _editor->begin_reversible_command (_("change fade out length"));
2864 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2866 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2872 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2873 XMLNode &before = alist->get_state();
2875 tmp->audio_region()->set_fade_out_length (fade_length);
2876 tmp->audio_region()->set_fade_out_active (true);
2878 XMLNode &after = alist->get_state();
2879 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2882 _editor->commit_reversible_command ();
2886 FadeOutDrag::aborted (bool)
2888 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2889 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2895 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2899 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2902 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2904 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2907 _points.push_back (ArdourCanvas::Duple (0, 0));
2908 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2911 MarkerDrag::~MarkerDrag ()
2913 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2918 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2920 location = new Location (*l);
2921 markers.push_back (m);
2926 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2928 Drag::start_grab (event, cursor);
2932 Location *location = _editor->find_location_from_marker (_marker, is_start);
2933 _editor->_dragging_edit_point = true;
2935 update_item (location);
2937 // _drag_line->show();
2938 // _line->raise_to_top();
2941 show_verbose_cursor_time (location->start());
2943 show_verbose_cursor_time (location->end());
2946 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2949 case Selection::Toggle:
2950 /* we toggle on the button release */
2952 case Selection::Set:
2953 if (!_editor->selection->selected (_marker)) {
2954 _editor->selection->set (_marker);
2957 case Selection::Extend:
2959 Locations::LocationList ll;
2960 list<Marker*> to_add;
2962 _editor->selection->markers.range (s, e);
2963 s = min (_marker->position(), s);
2964 e = max (_marker->position(), e);
2967 if (e < max_framepos) {
2970 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2971 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2972 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2975 to_add.push_back (lm->start);
2978 to_add.push_back (lm->end);
2982 if (!to_add.empty()) {
2983 _editor->selection->add (to_add);
2987 case Selection::Add:
2988 _editor->selection->add (_marker);
2992 /* Set up copies for us to manipulate during the drag
2995 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2997 Location* l = _editor->find_location_from_marker (*i, is_start);
3004 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3006 /* range: check that the other end of the range isn't
3009 CopiedLocationInfo::iterator x;
3010 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3011 if (*(*x).location == *l) {
3015 if (x == _copied_locations.end()) {
3016 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3018 (*x).markers.push_back (*i);
3019 (*x).move_both = true;
3027 MarkerDrag::setup_pointer_frame_offset ()
3030 Location *location = _editor->find_location_from_marker (_marker, is_start);
3031 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3035 MarkerDrag::motion (GdkEvent* event, bool)
3037 framecnt_t f_delta = 0;
3039 bool move_both = false;
3040 Location *real_location;
3041 Location *copy_location = 0;
3043 framepos_t const newframe = adjusted_current_frame (event);
3044 framepos_t next = newframe;
3046 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3050 CopiedLocationInfo::iterator x;
3052 /* find the marker we're dragging, and compute the delta */
3054 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3056 copy_location = (*x).location;
3058 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3060 /* this marker is represented by this
3061 * CopiedLocationMarkerInfo
3064 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3069 if (real_location->is_mark()) {
3070 f_delta = newframe - copy_location->start();
3074 switch (_marker->type()) {
3075 case Marker::SessionStart:
3076 case Marker::RangeStart:
3077 case Marker::LoopStart:
3078 case Marker::PunchIn:
3079 f_delta = newframe - copy_location->start();
3082 case Marker::SessionEnd:
3083 case Marker::RangeEnd:
3084 case Marker::LoopEnd:
3085 case Marker::PunchOut:
3086 f_delta = newframe - copy_location->end();
3089 /* what kind of marker is this ? */
3098 if (x == _copied_locations.end()) {
3099 /* hmm, impossible - we didn't find the dragged marker */
3103 /* now move them all */
3105 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3107 copy_location = x->location;
3109 /* call this to find out if its the start or end */
3111 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3115 if (real_location->locked()) {
3119 if (copy_location->is_mark()) {
3123 copy_location->set_start (copy_location->start() + f_delta);
3127 framepos_t new_start = copy_location->start() + f_delta;
3128 framepos_t new_end = copy_location->end() + f_delta;
3130 if (is_start) { // start-of-range marker
3132 if (move_both || (*x).move_both) {
3133 copy_location->set_start (new_start);
3134 copy_location->set_end (new_end);
3135 } else if (new_start < copy_location->end()) {
3136 copy_location->set_start (new_start);
3137 } else if (newframe > 0) {
3138 _editor->snap_to (next, 1, true);
3139 copy_location->set_end (next);
3140 copy_location->set_start (newframe);
3143 } else { // end marker
3145 if (move_both || (*x).move_both) {
3146 copy_location->set_end (new_end);
3147 copy_location->set_start (new_start);
3148 } else if (new_end > copy_location->start()) {
3149 copy_location->set_end (new_end);
3150 } else if (newframe > 0) {
3151 _editor->snap_to (next, -1, true);
3152 copy_location->set_start (next);
3153 copy_location->set_end (newframe);
3158 update_item (copy_location);
3160 /* now lookup the actual GUI items used to display this
3161 * location and move them to wherever the copy of the location
3162 * is now. This means that the logic in ARDOUR::Location is
3163 * still enforced, even though we are not (yet) modifying
3164 * the real Location itself.
3167 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3170 lm->set_position (copy_location->start(), copy_location->end());
3175 assert (!_copied_locations.empty());
3177 show_verbose_cursor_time (newframe);
3181 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3183 if (!movement_occurred) {
3185 if (was_double_click()) {
3186 _editor->rename_marker (_marker);
3190 /* just a click, do nothing but finish
3191 off the selection process
3194 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3197 case Selection::Set:
3198 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3199 _editor->selection->set (_marker);
3203 case Selection::Toggle:
3204 /* we toggle on the button release, click only */
3205 _editor->selection->toggle (_marker);
3208 case Selection::Extend:
3209 case Selection::Add:
3216 _editor->_dragging_edit_point = false;
3218 _editor->begin_reversible_command ( _("move marker") );
3219 XMLNode &before = _editor->session()->locations()->get_state();
3221 MarkerSelection::iterator i;
3222 CopiedLocationInfo::iterator x;
3225 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3226 x != _copied_locations.end() && i != _editor->selection->markers.end();
3229 Location * location = _editor->find_location_from_marker (*i, is_start);
3233 if (location->locked()) {
3237 if (location->is_mark()) {
3238 location->set_start (((*x).location)->start());
3240 location->set (((*x).location)->start(), ((*x).location)->end());
3245 XMLNode &after = _editor->session()->locations()->get_state();
3246 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3247 _editor->commit_reversible_command ();
3251 MarkerDrag::aborted (bool)
3257 MarkerDrag::update_item (Location*)
3262 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3264 _cumulative_x_drag (0),
3265 _cumulative_y_drag (0)
3267 if (_zero_gain_fraction < 0.0) {
3268 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3271 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3273 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3279 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3281 Drag::start_grab (event, _editor->cursors()->fader);
3283 // start the grab at the center of the control point so
3284 // the point doesn't 'jump' to the mouse after the first drag
3285 _fixed_grab_x = _point->get_x();
3286 _fixed_grab_y = _point->get_y();
3288 float const fraction = 1 - (_point->get_y() / _point->line().height());
3290 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3292 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3293 event->button.x + 10, event->button.y + 10);
3295 _editor->verbose_cursor()->show ();
3297 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3299 if (!_point->can_slide ()) {
3300 _x_constrained = true;
3305 ControlPointDrag::motion (GdkEvent* event, bool)
3307 double dx = _drags->current_pointer_x() - last_pointer_x();
3308 double dy = current_pointer_y() - last_pointer_y();
3310 if (event->button.state & Keyboard::SecondaryModifier) {
3315 /* coordinate in pixels relative to the start of the region (for region-based automation)
3316 or track (for track-based automation) */
3317 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3318 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3320 // calculate zero crossing point. back off by .01 to stay on the
3321 // positive side of zero
3322 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3324 // make sure we hit zero when passing through
3325 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3329 if (_x_constrained) {
3332 if (_y_constrained) {
3336 _cumulative_x_drag = cx - _fixed_grab_x;
3337 _cumulative_y_drag = cy - _fixed_grab_y;
3341 cy = min ((double) _point->line().height(), cy);
3343 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3345 if (!_x_constrained) {
3346 _editor->snap_to_with_modifier (cx_frames, event);
3349 cx_frames = min (cx_frames, _point->line().maximum_time());
3351 float const fraction = 1.0 - (cy / _point->line().height());
3353 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3355 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3359 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3361 if (!movement_occurred) {
3365 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3366 _editor->reset_point_selection ();
3370 motion (event, false);
3373 _point->line().end_drag (_pushing, _final_index);
3374 _editor->session()->commit_reversible_command ();
3378 ControlPointDrag::aborted (bool)
3380 _point->line().reset ();
3384 ControlPointDrag::active (Editing::MouseMode m)
3386 if (m == Editing::MouseGain) {
3387 /* always active in mouse gain */
3391 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3392 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3395 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3398 _cumulative_y_drag (0)
3400 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3404 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3406 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3409 _item = &_line->grab_item ();
3411 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3412 origin, and ditto for y.
3415 double cx = event->button.x;
3416 double cy = event->button.y;
3418 _line->parent_group().canvas_to_item (cx, cy);
3420 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3425 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3426 /* no adjacent points */
3430 Drag::start_grab (event, _editor->cursors()->fader);
3432 /* store grab start in parent frame */
3437 double fraction = 1.0 - (cy / _line->height());
3439 _line->start_drag_line (before, after, fraction);
3441 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3442 event->button.x + 10, event->button.y + 10);
3444 _editor->verbose_cursor()->show ();
3448 LineDrag::motion (GdkEvent* event, bool)
3450 double dy = current_pointer_y() - last_pointer_y();
3452 if (event->button.state & Keyboard::SecondaryModifier) {
3456 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3458 _cumulative_y_drag = cy - _fixed_grab_y;
3461 cy = min ((double) _line->height(), cy);
3463 double const fraction = 1.0 - (cy / _line->height());
3466 /* we are ignoring x position for this drag, so we can just pass in anything */
3467 _line->drag_motion (0, fraction, true, false, ignored);
3469 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3473 LineDrag::finished (GdkEvent* event, bool movement_occured)
3475 if (movement_occured) {
3476 motion (event, false);
3477 _line->end_drag (false, 0);
3479 /* add a new control point on the line */
3481 AutomationTimeAxisView* atv;
3483 _line->end_drag (false, 0);
3485 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3486 framepos_t where = _editor->window_event_sample (event, 0, 0);
3487 atv->add_automation_event (event, where, event->button.y, false);
3491 _editor->session()->commit_reversible_command ();
3495 LineDrag::aborted (bool)
3500 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3503 _cumulative_x_drag (0)
3505 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3509 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3511 Drag::start_grab (event);
3513 _line = reinterpret_cast<Line*> (_item);
3516 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3518 double cx = event->button.x;
3519 double cy = event->button.y;
3521 _item->parent()->canvas_to_item (cx, cy);
3523 /* store grab start in parent frame */
3524 _region_view_grab_x = cx;
3526 _before = *(float*) _item->get_data ("position");
3528 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3530 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3534 FeatureLineDrag::motion (GdkEvent*, bool)
3536 double dx = _drags->current_pointer_x() - last_pointer_x();
3538 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3540 _cumulative_x_drag += dx;
3542 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3551 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3553 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3555 float *pos = new float;
3558 _line->set_data ("position", pos);
3564 FeatureLineDrag::finished (GdkEvent*, bool)
3566 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3567 _arv->update_transient(_before, _before);
3571 FeatureLineDrag::aborted (bool)
3576 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3578 , _vertical_only (false)
3580 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3584 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3586 Drag::start_grab (event);
3587 show_verbose_cursor_time (adjusted_current_frame (event));
3591 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3598 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3600 framepos_t grab = grab_frame ();
3601 if (Config->get_rubberbanding_snaps_to_grid ()) {
3602 _editor->snap_to_with_modifier (grab, event);
3605 /* base start and end on initial click position */
3615 if (current_pointer_y() < grab_y()) {
3616 y1 = current_pointer_y();
3619 y2 = current_pointer_y();
3623 if (start != end || y1 != y2) {
3625 double x1 = _editor->sample_to_pixel (start);
3626 double x2 = _editor->sample_to_pixel (end);
3627 const double min_dimension = 2.0;
3629 if (_vertical_only) {
3630 /* fixed 10 pixel width */
3634 x2 = min (x1 - min_dimension, x2);
3636 x2 = max (x1 + min_dimension, x2);
3641 y2 = min (y1 - min_dimension, y2);
3643 y2 = max (y1 + min_dimension, y2);
3646 /* translate rect into item space and set */
3648 Rect r (x1, y1, x2, y2);
3650 /* this drag is a _trackview_only == true drag, so the y1 and
3651 * y2 (computed using current_pointer_y() and grab_y()) will be
3652 * relative to the top of the trackview group). The
3653 * rubberband rect has the same parent/scroll offset as the
3654 * the trackview group, so we can use the "r" rect directly
3655 * to set the shape of the rubberband.
3658 _editor->rubberband_rect->set (r);
3659 _editor->rubberband_rect->show();
3660 _editor->rubberband_rect->raise_to_top();
3662 show_verbose_cursor_time (pf);
3664 do_select_things (event, true);
3669 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3674 if (grab_frame() < last_pointer_frame()) {
3676 x2 = last_pointer_frame ();
3679 x1 = last_pointer_frame ();
3685 if (current_pointer_y() < grab_y()) {
3686 y1 = current_pointer_y();
3689 y2 = current_pointer_y();
3693 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3697 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3699 if (movement_occurred) {
3701 motion (event, false);
3702 do_select_things (event, false);
3708 bool do_deselect = true;
3709 MidiTimeAxisView* mtv;
3711 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3713 if (_editor->selection->empty()) {
3714 /* nothing selected */
3715 add_midi_region (mtv);
3716 do_deselect = false;
3720 /* do not deselect if Primary or Tertiary (toggle-select or
3721 * extend-select are pressed.
3724 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3725 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3732 _editor->rubberband_rect->hide();
3736 RubberbandSelectDrag::aborted (bool)
3738 _editor->rubberband_rect->hide ();
3741 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3742 : RegionDrag (e, i, p, v)
3744 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3748 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3750 Drag::start_grab (event, cursor);
3752 show_verbose_cursor_time (adjusted_current_frame (event));
3756 TimeFXDrag::motion (GdkEvent* event, bool)
3758 RegionView* rv = _primary;
3759 StreamView* cv = rv->get_time_axis_view().view ();
3761 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3762 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3763 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3765 framepos_t const pf = adjusted_current_frame (event);
3767 if (pf > rv->region()->position()) {
3768 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3771 show_verbose_cursor_time (pf);
3775 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3777 _primary->get_time_axis_view().hide_timestretch ();
3779 if (!movement_occurred) {
3783 if (last_pointer_frame() < _primary->region()->position()) {
3784 /* backwards drag of the left edge - not usable */
3788 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3790 float percentage = (double) newlen / (double) _primary->region()->length();
3792 #ifndef USE_RUBBERBAND
3793 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3794 if (_primary->region()->data_type() == DataType::AUDIO) {
3795 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3799 if (!_editor->get_selection().regions.empty()) {
3800 /* primary will already be included in the selection, and edit
3801 group shared editing will propagate selection across
3802 equivalent regions, so just use the current region
3806 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3807 error << _("An error occurred while executing time stretch operation") << endmsg;
3813 TimeFXDrag::aborted (bool)
3815 _primary->get_time_axis_view().hide_timestretch ();
3818 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3821 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3825 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3827 Drag::start_grab (event);
3831 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3833 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3837 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3839 if (movement_occurred && _editor->session()) {
3840 /* make sure we stop */
3841 _editor->session()->request_transport_speed (0.0);
3846 ScrubDrag::aborted (bool)
3851 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3856 , _original_pointer_time_axis (-1)
3857 , _last_pointer_time_axis (-1)
3858 , _time_selection_at_start (!_editor->get_selection().time.empty())
3860 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3862 if (_time_selection_at_start) {
3863 start_at_start = _editor->get_selection().time.start();
3864 end_at_start = _editor->get_selection().time.end_frame();
3869 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3871 if (_editor->session() == 0) {
3875 Gdk::Cursor* cursor = 0;
3877 switch (_operation) {
3878 case CreateSelection:
3879 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3884 cursor = _editor->cursors()->selector;
3885 Drag::start_grab (event, cursor);
3888 case SelectionStartTrim:
3889 if (_editor->clicked_axisview) {
3890 _editor->clicked_axisview->order_selection_trims (_item, true);
3892 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3895 case SelectionEndTrim:
3896 if (_editor->clicked_axisview) {
3897 _editor->clicked_axisview->order_selection_trims (_item, false);
3899 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3903 Drag::start_grab (event, cursor);
3906 case SelectionExtend:
3907 Drag::start_grab (event, cursor);
3911 if (_operation == SelectionMove) {
3912 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3914 show_verbose_cursor_time (adjusted_current_frame (event));
3917 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
3921 SelectionDrag::setup_pointer_frame_offset ()
3923 switch (_operation) {
3924 case CreateSelection:
3925 _pointer_frame_offset = 0;
3928 case SelectionStartTrim:
3930 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3933 case SelectionEndTrim:
3934 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3937 case SelectionExtend:
3943 SelectionDrag::motion (GdkEvent* event, bool first_move)
3945 framepos_t start = 0;
3947 framecnt_t length = 0;
3948 framecnt_t distance = 0;
3950 framepos_t const pending_position = adjusted_current_frame (event);
3952 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
3956 switch (_operation) {
3957 case CreateSelection:
3959 framepos_t grab = grab_frame ();
3962 grab = adjusted_current_frame (event, false);
3963 if (grab < pending_position) {
3964 _editor->snap_to (grab, -1);
3966 _editor->snap_to (grab, 1);
3970 if (pending_position < grab) {
3971 start = pending_position;
3974 end = pending_position;
3978 /* first drag: Either add to the selection
3979 or create a new selection
3986 /* adding to the selection */
3987 _editor->set_selected_track_as_side_effect (Selection::Add);
3988 _editor->clicked_selection = _editor->selection->add (start, end);
3995 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3996 _editor->set_selected_track_as_side_effect (Selection::Set);
3999 _editor->clicked_selection = _editor->selection->set (start, end);
4003 /* select all tracks within the rectangle that we've marked out so far */
4004 TrackViewList to_be_added_to_selection;
4005 TrackViewList to_be_removed_from_selection;
4006 TrackViewList& all_tracks (_editor->track_views);
4008 ArdourCanvas::Coord const top = grab_y();
4009 ArdourCanvas::Coord const bottom = current_pointer_y();
4011 if (top >= 0 && bottom >= 0) {
4013 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4015 if ((*i)->covered_by_y_range (top, bottom)) {
4016 if (!(*i)->get_selected()) {
4017 to_be_added_to_selection.push_back (*i);
4020 if ((*i)->get_selected()) {
4021 to_be_removed_from_selection.push_back (*i);
4026 if (!to_be_added_to_selection.empty()) {
4027 _editor->selection->add (to_be_added_to_selection);
4030 if (!to_be_removed_from_selection.empty()) {
4031 _editor->selection->remove (to_be_removed_from_selection);
4037 case SelectionStartTrim:
4039 start = _editor->selection->time[_editor->clicked_selection].start;
4040 end = _editor->selection->time[_editor->clicked_selection].end;
4042 if (pending_position > end) {
4045 start = pending_position;
4049 case SelectionEndTrim:
4051 start = _editor->selection->time[_editor->clicked_selection].start;
4052 end = _editor->selection->time[_editor->clicked_selection].end;
4054 if (pending_position < start) {
4057 end = pending_position;
4064 start = _editor->selection->time[_editor->clicked_selection].start;
4065 end = _editor->selection->time[_editor->clicked_selection].end;
4067 length = end - start;
4068 distance = pending_position - start;
4069 start = pending_position;
4070 _editor->snap_to (start);
4072 end = start + length;
4076 case SelectionExtend:
4081 switch (_operation) {
4083 if (_time_selection_at_start) {
4084 _editor->selection->move_time (distance);
4088 _editor->selection->replace (_editor->clicked_selection, start, end);
4092 if (_operation == SelectionMove) {
4093 show_verbose_cursor_time(start);
4095 show_verbose_cursor_time(pending_position);
4100 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4102 Session* s = _editor->session();
4104 if (movement_occurred) {
4105 motion (event, false);
4106 /* XXX this is not object-oriented programming at all. ick */
4107 if (_editor->selection->time.consolidate()) {
4108 _editor->selection->TimeChanged ();
4111 /* XXX what if its a music time selection? */
4113 if ( s->get_play_range() && s->transport_rolling() ) {
4114 s->request_play_range (&_editor->selection->time, true);
4116 if (Config->get_always_play_range() && !s->transport_rolling()) {
4117 s->request_locate (_editor->get_selection().time.start());
4123 /* just a click, no pointer movement.
4126 if (_operation == SelectionExtend) {
4127 if (_time_selection_at_start) {
4128 framepos_t pos = adjusted_current_frame (event, false);
4129 framepos_t start = min (pos, start_at_start);
4130 framepos_t end = max (pos, end_at_start);
4131 _editor->selection->set (start, end);
4134 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4135 if (_editor->clicked_selection) {
4136 _editor->selection->remove (_editor->clicked_selection);
4139 if (!_editor->clicked_selection) {
4140 _editor->selection->clear_time();
4145 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4146 _editor->selection->set (_editor->clicked_axisview);
4149 if (s && s->get_play_range () && s->transport_rolling()) {
4150 s->request_stop (false, false);
4155 _editor->stop_canvas_autoscroll ();
4156 _editor->clicked_selection = 0;
4160 SelectionDrag::aborted (bool)
4165 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4166 : Drag (e, i, false),
4170 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4172 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4173 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4174 physical_screen_height (_editor->get_window())));
4175 _drag_rect->hide ();
4177 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4178 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4182 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4184 if (_editor->session() == 0) {
4188 Gdk::Cursor* cursor = 0;
4190 if (!_editor->temp_location) {
4191 _editor->temp_location = new Location (*_editor->session());
4194 switch (_operation) {
4195 case CreateRangeMarker:
4196 case CreateTransportMarker:
4197 case CreateCDMarker:
4199 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4204 cursor = _editor->cursors()->selector;
4208 Drag::start_grab (event, cursor);
4210 show_verbose_cursor_time (adjusted_current_frame (event));
4214 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4216 framepos_t start = 0;
4218 ArdourCanvas::Rectangle *crect;
4220 switch (_operation) {
4221 case CreateRangeMarker:
4222 crect = _editor->range_bar_drag_rect;
4224 case CreateTransportMarker:
4225 crect = _editor->transport_bar_drag_rect;
4227 case CreateCDMarker:
4228 crect = _editor->cd_marker_bar_drag_rect;
4231 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4236 framepos_t const pf = adjusted_current_frame (event);
4238 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4239 framepos_t grab = grab_frame ();
4240 _editor->snap_to (grab);
4242 if (pf < grab_frame()) {
4250 /* first drag: Either add to the selection
4251 or create a new selection.
4256 _editor->temp_location->set (start, end);
4260 update_item (_editor->temp_location);
4262 //_drag_rect->raise_to_top();
4268 _editor->temp_location->set (start, end);
4270 double x1 = _editor->sample_to_pixel (start);
4271 double x2 = _editor->sample_to_pixel (end);
4275 update_item (_editor->temp_location);
4278 show_verbose_cursor_time (pf);
4283 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4285 Location * newloc = 0;
4289 if (movement_occurred) {
4290 motion (event, false);
4293 switch (_operation) {
4294 case CreateRangeMarker:
4295 case CreateCDMarker:
4297 _editor->begin_reversible_command (_("new range marker"));
4298 XMLNode &before = _editor->session()->locations()->get_state();
4299 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4300 if (_operation == CreateCDMarker) {
4301 flags = Location::IsRangeMarker | Location::IsCDMarker;
4302 _editor->cd_marker_bar_drag_rect->hide();
4305 flags = Location::IsRangeMarker;
4306 _editor->range_bar_drag_rect->hide();
4308 newloc = new Location (
4309 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4312 _editor->session()->locations()->add (newloc, true);
4313 XMLNode &after = _editor->session()->locations()->get_state();
4314 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4315 _editor->commit_reversible_command ();
4319 case CreateTransportMarker:
4320 // popup menu to pick loop or punch
4321 _editor->new_transport_marker_context_menu (&event->button, _item);
4327 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4329 if (_operation == CreateTransportMarker) {
4331 /* didn't drag, so just locate */
4333 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4335 } else if (_operation == CreateCDMarker) {
4337 /* didn't drag, but mark is already created so do
4340 } else { /* operation == CreateRangeMarker */
4346 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4348 if (end == max_framepos) {
4349 end = _editor->session()->current_end_frame ();
4352 if (start == max_framepos) {
4353 start = _editor->session()->current_start_frame ();
4356 switch (_editor->mouse_mode) {
4358 /* find the two markers on either side and then make the selection from it */
4359 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4363 /* find the two markers on either side of the click and make the range out of it */
4364 _editor->selection->set (start, end);
4373 _editor->stop_canvas_autoscroll ();
4377 RangeMarkerBarDrag::aborted (bool)
4383 RangeMarkerBarDrag::update_item (Location* location)
4385 double const x1 = _editor->sample_to_pixel (location->start());
4386 double const x2 = _editor->sample_to_pixel (location->end());
4388 _drag_rect->set_x0 (x1);
4389 _drag_rect->set_x1 (x2);
4392 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4396 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4400 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4402 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4403 Drag::start_grab (event, _editor->cursors()->zoom_out);
4406 Drag::start_grab (event, _editor->cursors()->zoom_in);
4410 show_verbose_cursor_time (adjusted_current_frame (event));
4414 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4419 framepos_t const pf = adjusted_current_frame (event);
4421 framepos_t grab = grab_frame ();
4422 _editor->snap_to_with_modifier (grab, event);
4424 /* base start and end on initial click position */
4436 _editor->zoom_rect->show();
4437 _editor->zoom_rect->raise_to_top();
4440 _editor->reposition_zoom_rect(start, end);
4442 show_verbose_cursor_time (pf);
4447 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4449 if (movement_occurred) {
4450 motion (event, false);
4452 if (grab_frame() < last_pointer_frame()) {
4453 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4455 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4458 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4459 _editor->tav_zoom_step (_zoom_out);
4461 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4465 _editor->zoom_rect->hide();
4469 MouseZoomDrag::aborted (bool)
4471 _editor->zoom_rect->hide ();
4474 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4476 , _cumulative_dx (0)
4477 , _cumulative_dy (0)
4479 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4481 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4483 _region = &_primary->region_view ();
4484 _note_height = _region->midi_stream_view()->note_height ();
4488 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4490 Drag::start_grab (event);
4492 if (!(_was_selected = _primary->selected())) {
4494 /* tertiary-click means extend selection - we'll do that on button release,
4495 so don't add it here, because otherwise we make it hard to figure
4496 out the "extend-to" range.
4499 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4502 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4505 _region->note_selected (_primary, true);
4507 _region->unique_select (_primary);
4513 /** @return Current total drag x change in frames */
4515 NoteDrag::total_dx () const
4518 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4520 /* primary note time */
4521 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4523 /* new time of the primary note in session frames */
4524 frameoffset_t st = n + dx;
4526 framepos_t const rp = _region->region()->position ();
4528 /* prevent the note being dragged earlier than the region's position */
4531 /* snap and return corresponding delta */
4532 return _region->snap_frame_to_frame (st - rp) + rp - n;
4535 /** @return Current total drag y change in note number */
4537 NoteDrag::total_dy () const
4539 MidiStreamView* msv = _region->midi_stream_view ();
4540 double const y = _region->midi_view()->y_position ();
4541 /* new current note */
4542 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4544 n = max (msv->lowest_note(), n);
4545 n = min (msv->highest_note(), n);
4546 /* and work out delta */
4547 return n - msv->y_to_note (grab_y() - y);
4551 NoteDrag::motion (GdkEvent *, bool)
4553 /* Total change in x and y since the start of the drag */
4554 frameoffset_t const dx = total_dx ();
4555 int8_t const dy = total_dy ();
4557 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4558 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4559 double const tdy = -dy * _note_height - _cumulative_dy;
4562 _cumulative_dx += tdx;
4563 _cumulative_dy += tdy;
4565 int8_t note_delta = total_dy();
4567 _region->move_selection (tdx, tdy, note_delta);
4569 /* the new note value may be the same as the old one, but we
4570 * don't know what that means because the selection may have
4571 * involved more than one note and we might be doing something
4572 * odd with them. so show the note value anyway, always.
4576 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4578 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4579 (int) floor ((double)new_note));
4581 show_verbose_cursor_text (buf);
4586 NoteDrag::finished (GdkEvent* ev, bool moved)
4589 /* no motion - select note */
4591 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4592 _editor->current_mouse_mode() == Editing::MouseDraw) {
4594 if (_was_selected) {
4595 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4597 _region->note_deselected (_primary);
4600 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4601 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4603 if (!extend && !add && _region->selection_size() > 1) {
4604 _region->unique_select (_primary);
4605 } else if (extend) {
4606 _region->note_selected (_primary, true, true);
4608 /* it was added during button press */
4613 _region->note_dropped (_primary, total_dx(), total_dy());
4618 NoteDrag::aborted (bool)
4623 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4624 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4625 : Drag (editor, atv->base_item ())
4627 , _nothing_to_drag (false)
4629 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4630 y_origin = atv->y_position();
4631 setup (atv->lines ());
4634 /** Make an AutomationRangeDrag for region gain lines */
4635 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4636 : Drag (editor, rv->get_canvas_group ())
4638 , _nothing_to_drag (false)
4640 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4642 list<boost::shared_ptr<AutomationLine> > lines;
4643 lines.push_back (rv->get_gain_line ());
4644 y_origin = rv->get_time_axis_view().y_position();
4648 /** @param lines AutomationLines to drag.
4649 * @param offset Offset from the session start to the points in the AutomationLines.
4652 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4654 /* find the lines that overlap the ranges being dragged */
4655 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4656 while (i != lines.end ()) {
4657 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4660 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4662 /* check this range against all the AudioRanges that we are using */
4663 list<AudioRange>::const_iterator k = _ranges.begin ();
4664 while (k != _ranges.end()) {
4665 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4671 /* add it to our list if it overlaps at all */
4672 if (k != _ranges.end()) {
4677 _lines.push_back (n);
4683 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4687 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4689 return 1.0 - ((global_y - y_origin) / line->height());
4693 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4695 Drag::start_grab (event, cursor);
4697 /* Get line states before we start changing things */
4698 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4699 i->state = &i->line->get_state ();
4700 i->original_fraction = y_fraction (i->line, current_pointer_y());
4703 if (_ranges.empty()) {
4705 /* No selected time ranges: drag all points */
4706 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4707 uint32_t const N = i->line->npoints ();
4708 for (uint32_t j = 0; j < N; ++j) {
4709 i->points.push_back (i->line->nth (j));
4715 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4717 framecnt_t const half = (i->start + i->end) / 2;
4719 /* find the line that this audio range starts in */
4720 list<Line>::iterator j = _lines.begin();
4721 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4725 if (j != _lines.end()) {
4726 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4728 /* j is the line that this audio range starts in; fade into it;
4729 64 samples length plucked out of thin air.
4732 framepos_t a = i->start + 64;
4737 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4738 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4740 the_list->add (p, the_list->eval (p));
4741 the_list->add (q, the_list->eval (q));
4744 /* same thing for the end */
4747 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4751 if (j != _lines.end()) {
4752 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4754 /* j is the line that this audio range starts in; fade out of it;
4755 64 samples length plucked out of thin air.
4758 framepos_t b = i->end - 64;
4763 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4764 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4766 the_list->add (p, the_list->eval (p));
4767 the_list->add (q, the_list->eval (q));
4771 _nothing_to_drag = true;
4773 /* Find all the points that should be dragged and put them in the relevant
4774 points lists in the Line structs.
4777 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4779 uint32_t const N = i->line->npoints ();
4780 for (uint32_t j = 0; j < N; ++j) {
4782 /* here's a control point on this line */
4783 ControlPoint* p = i->line->nth (j);
4784 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4786 /* see if it's inside a range */
4787 list<AudioRange>::const_iterator k = _ranges.begin ();
4788 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4792 if (k != _ranges.end()) {
4793 /* dragging this point */
4794 _nothing_to_drag = false;
4795 i->points.push_back (p);
4801 if (_nothing_to_drag) {
4805 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4806 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
4811 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4813 if (_nothing_to_drag) {
4817 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4818 float const f = y_fraction (l->line, current_pointer_y());
4819 /* we are ignoring x position for this drag, so we can just pass in anything */
4821 l->line->drag_motion (0, f, true, false, ignored);
4822 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4827 AutomationRangeDrag::finished (GdkEvent* event, bool)
4829 if (_nothing_to_drag) {
4833 motion (event, false);
4834 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4835 i->line->end_drag (false, 0);
4838 _editor->session()->commit_reversible_command ();
4842 AutomationRangeDrag::aborted (bool)
4844 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4849 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4852 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4853 layer = v->region()->layer ();
4854 initial_y = v->get_canvas_group()->position().y;
4855 initial_playlist = v->region()->playlist ();
4856 initial_position = v->region()->position ();
4857 initial_end = v->region()->position () + v->region()->length ();
4860 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4861 : Drag (e, i->canvas_item ())
4864 , _cumulative_dx (0)
4866 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4867 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4872 PatchChangeDrag::motion (GdkEvent* ev, bool)
4874 framepos_t f = adjusted_current_frame (ev);
4875 boost::shared_ptr<Region> r = _region_view->region ();
4876 f = max (f, r->position ());
4877 f = min (f, r->last_frame ());
4879 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4880 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4881 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4882 _cumulative_dx = dxu;
4886 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4888 if (!movement_occurred) {
4892 boost::shared_ptr<Region> r (_region_view->region ());
4893 framepos_t f = adjusted_current_frame (ev);
4894 f = max (f, r->position ());
4895 f = min (f, r->last_frame ());
4897 _region_view->move_patch_change (
4899 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4904 PatchChangeDrag::aborted (bool)
4906 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4910 PatchChangeDrag::setup_pointer_frame_offset ()
4912 boost::shared_ptr<Region> region = _region_view->region ();
4913 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4916 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4917 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4924 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4926 framepos_t const p = _region_view->region()->position ();
4927 double const y = _region_view->midi_view()->y_position ();
4929 x1 = max ((framepos_t) 0, x1 - p);
4930 x2 = max ((framepos_t) 0, x2 - p);
4931 y1 = max (0.0, y1 - y);
4932 y2 = max (0.0, y2 - y);
4934 _region_view->update_drag_selection (
4935 _editor->sample_to_pixel (x1),
4936 _editor->sample_to_pixel (x2),
4939 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4944 MidiRubberbandSelectDrag::deselect_things ()
4949 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4950 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4953 _vertical_only = true;
4957 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4959 double const y = _region_view->midi_view()->y_position ();
4961 y1 = max (0.0, y1 - y);
4962 y2 = max (0.0, y2 - y);
4964 _region_view->update_vertical_drag_selection (
4967 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4972 MidiVerticalSelectDrag::deselect_things ()
4977 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4978 : RubberbandSelectDrag (e, i)
4984 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4986 if (drag_in_progress) {
4987 /* We just want to select things at the end of the drag, not during it */
4991 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4993 _editor->begin_reversible_command (_("rubberband selection"));
4994 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4995 _editor->commit_reversible_command ();
4999 EditorRubberbandSelectDrag::deselect_things ()
5001 if (!getenv("ARDOUR_SAE")) {
5002 _editor->selection->clear_tracks();
5004 _editor->selection->clear_regions();
5005 _editor->selection->clear_points ();
5006 _editor->selection->clear_lines ();
5009 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5017 NoteCreateDrag::~NoteCreateDrag ()
5023 NoteCreateDrag::grid_frames (framepos_t t) const
5026 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
5031 return _region_view->region_beats_to_region_frames (grid_beats);
5035 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5037 Drag::start_grab (event, cursor);
5039 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5041 framepos_t pf = _drags->current_pointer_frame ();
5042 framecnt_t const g = grid_frames (pf);
5044 /* Hack so that we always snap to the note that we are over, instead of snapping
5045 to the next one if we're more than halfway through the one we're over.
5047 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5051 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5053 MidiStreamView* sv = _region_view->midi_stream_view ();
5054 double const x = _editor->sample_to_pixel (_note[0]);
5055 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5057 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5058 _drag_rect->set_outline_all ();
5059 _drag_rect->set_outline_color (0xffffff99);
5060 _drag_rect->set_fill_color (0xffffff66);
5064 NoteCreateDrag::motion (GdkEvent* event, bool)
5066 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5067 double const x = _editor->sample_to_pixel (_note[1]);
5068 if (_note[1] > _note[0]) {
5069 _drag_rect->set_x1 (x);
5071 _drag_rect->set_x0 (x);
5076 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5078 if (!had_movement) {
5082 framepos_t const start = min (_note[0], _note[1]);
5083 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5085 framecnt_t const g = grid_frames (start);
5086 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5088 if (_editor->snap_mode() == SnapNormal && length < g) {
5089 length = g - one_tick;
5092 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5094 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5098 NoteCreateDrag::y_to_region (double y) const
5101 _region_view->get_canvas_group()->canvas_to_item (x, y);
5106 NoteCreateDrag::aborted (bool)
5111 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5116 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5120 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5122 Drag::start_grab (event, cursor);
5126 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5132 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5135 distance = _drags->current_pointer_x() - grab_x();
5136 len = ar->fade_in()->back()->when;
5138 distance = grab_x() - _drags->current_pointer_x();
5139 len = ar->fade_out()->back()->when;
5142 /* how long should it be ? */
5144 new_length = len + _editor->pixel_to_sample (distance);
5146 /* now check with the region that this is legal */
5148 new_length = ar->verify_xfade_bounds (new_length, start);
5151 arv->reset_fade_in_shape_width (ar, new_length);
5153 arv->reset_fade_out_shape_width (ar, new_length);
5158 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5164 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5167 distance = _drags->current_pointer_x() - grab_x();
5168 len = ar->fade_in()->back()->when;
5170 distance = grab_x() - _drags->current_pointer_x();
5171 len = ar->fade_out()->back()->when;
5174 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5176 _editor->begin_reversible_command ("xfade trim");
5177 ar->playlist()->clear_owned_changes ();
5180 ar->set_fade_in_length (new_length);
5182 ar->set_fade_out_length (new_length);
5185 /* Adjusting the xfade may affect other regions in the playlist, so we need
5186 to get undo Commands from the whole playlist rather than just the
5190 vector<Command*> cmds;
5191 ar->playlist()->rdiff (cmds);
5192 _editor->session()->add_commands (cmds);
5193 _editor->commit_reversible_command ();
5198 CrossfadeEdgeDrag::aborted (bool)
5201 arv->redraw_start_xfade ();
5203 arv->redraw_end_xfade ();