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->set_opacity_for_drag (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 (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
942 i->view->set_opacity_for_drag (false);
945 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
950 if ((*i)->view()->layer_display() == Expanded) {
951 (*i)->view()->set_layer_display (Stacked);
957 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
959 RegionMotionDrag::finished (ev, movement_occurred);
961 if (!movement_occurred) {
965 if (was_double_click() && !_views.empty()) {
966 DraggingView dv = _views.front();
967 dv.view->show_region_editor ();
974 /* reverse this here so that we have the correct logic to finalize
978 if (Config->get_edit_mode() == Lock) {
979 _x_constrained = !_x_constrained;
982 assert (!_views.empty ());
984 /* We might have hidden region views so that they weren't visible during the drag
985 (when they have been reparented). Now everything can be shown again, as region
986 views are back in their track parent groups.
988 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
989 i->view->get_canvas_group()->show ();
992 bool const changed_position = (_last_frame_position != _primary->region()->position());
993 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
994 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1014 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1018 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region)
1020 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1025 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1026 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1027 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1028 return _editor->axis_view_from_route (audio_tracks.front());
1030 ChanCount one_midi_port (DataType::MIDI, 1);
1031 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1032 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1033 return _editor->axis_view_from_route (midi_tracks.front());
1036 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1042 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1044 RegionSelection new_views;
1045 PlaylistSet modified_playlists;
1046 RouteTimeAxisView* new_time_axis_view = 0;
1049 /* all changes were made during motion event handlers */
1051 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1055 _editor->commit_reversible_command ();
1059 if (_x_constrained) {
1060 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1062 _editor->begin_reversible_command (Operations::region_copy);
1065 /* insert the regions into their new playlists */
1066 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1068 RouteTimeAxisView* dest_rtv = 0;
1070 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1076 if (changed_position && !_x_constrained) {
1077 where = i->view->region()->position() - drag_delta;
1079 where = i->view->region()->position();
1082 if (i->time_axis_view < 0) {
1083 if (!new_time_axis_view) {
1084 new_time_axis_view = create_destination_time_axis (i->view->region());
1086 dest_rtv = new_time_axis_view;
1088 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1091 if (dest_rtv != 0) {
1092 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1093 if (new_view != 0) {
1094 new_views.push_back (new_view);
1098 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1099 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1102 list<DraggingView>::const_iterator next = i;
1108 /* If we've created new regions either by copying or moving
1109 to a new track, we want to replace the old selection with the new ones
1112 if (new_views.size() > 0) {
1113 _editor->selection->set (new_views);
1116 /* write commands for the accumulated diffs for all our modified playlists */
1117 add_stateful_diff_commands_for_playlists (modified_playlists);
1119 _editor->commit_reversible_command ();
1123 RegionMoveDrag::finished_no_copy (
1124 bool const changed_position,
1125 bool const changed_tracks,
1126 framecnt_t const drag_delta
1129 RegionSelection new_views;
1130 PlaylistSet modified_playlists;
1131 PlaylistSet frozen_playlists;
1132 set<RouteTimeAxisView*> views_to_update;
1133 RouteTimeAxisView* new_time_axis_view = 0;
1136 /* all changes were made during motion event handlers */
1137 _editor->commit_reversible_command ();
1141 if (_x_constrained) {
1142 _editor->begin_reversible_command (_("fixed time region drag"));
1144 _editor->begin_reversible_command (Operations::region_drag);
1147 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1149 RegionView* rv = i->view;
1150 RouteTimeAxisView* dest_rtv = 0;
1152 if (rv->region()->locked() || rv->region()->video_locked()) {
1157 if (i->time_axis_view < 0) {
1158 if (!new_time_axis_view) {
1159 new_time_axis_view = create_destination_time_axis (rv->region());
1161 dest_rtv = new_time_axis_view;
1163 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1168 double const dest_layer = i->layer;
1170 views_to_update.insert (dest_rtv);
1174 if (changed_position && !_x_constrained) {
1175 where = rv->region()->position() - drag_delta;
1177 where = rv->region()->position();
1180 if (changed_tracks) {
1182 /* insert into new playlist */
1184 RegionView* new_view = insert_region_into_playlist (
1185 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1188 if (new_view == 0) {
1193 new_views.push_back (new_view);
1195 /* remove from old playlist */
1197 /* the region that used to be in the old playlist is not
1198 moved to the new one - we use a copy of it. as a result,
1199 any existing editor for the region should no longer be
1202 rv->hide_region_editor();
1203 rv->set_opacity_for_drag (false);
1205 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1209 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1211 /* this movement may result in a crossfade being modified, or a layering change,
1212 so we need to get undo data from the playlist as well as the region.
1215 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1217 playlist->clear_changes ();
1220 rv->region()->clear_changes ();
1223 motion on the same track. plonk the previously reparented region
1224 back to its original canvas group (its streamview).
1225 No need to do anything for copies as they are fake regions which will be deleted.
1228 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1229 rv->get_canvas_group()->set_y_position (i->initial_y);
1232 /* just change the model */
1233 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1234 playlist->set_layer (rv->region(), dest_layer);
1237 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1239 r = frozen_playlists.insert (playlist);
1242 playlist->freeze ();
1245 rv->region()->set_position (where);
1247 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1250 if (changed_tracks) {
1252 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1253 was selected in all of them, then removing it from a playlist will have removed all
1254 trace of it from _views (i.e. there were N regions selected, we removed 1,
1255 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1256 corresponding regionview, and _views is now empty).
1258 This could have invalidated any and all iterators into _views.
1260 The heuristic we use here is: if the region selection is empty, break out of the loop
1261 here. if the region selection is not empty, then restart the loop because we know that
1262 we must have removed at least the region(view) we've just been working on as well as any
1263 that we processed on previous iterations.
1265 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1266 we can just iterate.
1270 if (_views.empty()) {
1281 /* If we've created new regions either by copying or moving
1282 to a new track, we want to replace the old selection with the new ones
1285 if (new_views.size() > 0) {
1286 _editor->selection->set (new_views);
1289 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1293 /* write commands for the accumulated diffs for all our modified playlists */
1294 add_stateful_diff_commands_for_playlists (modified_playlists);
1296 _editor->commit_reversible_command ();
1298 /* We have futzed with the layering of canvas items on our streamviews.
1299 If any region changed layer, this will have resulted in the stream
1300 views being asked to set up their region views, and all will be well.
1301 If not, we might now have badly-ordered region views. Ask the StreamViews
1302 involved to sort themselves out, just in case.
1305 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1306 (*i)->view()->playlist_layered ((*i)->track ());
1310 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1311 * @param region Region to remove.
1312 * @param playlist playlist To remove from.
1313 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1314 * that clear_changes () is only called once per playlist.
1317 RegionMoveDrag::remove_region_from_playlist (
1318 boost::shared_ptr<Region> region,
1319 boost::shared_ptr<Playlist> playlist,
1320 PlaylistSet& modified_playlists
1323 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1326 playlist->clear_changes ();
1329 playlist->remove_region (region);
1333 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1334 * clearing the playlist's diff history first if necessary.
1335 * @param region Region to insert.
1336 * @param dest_rtv Destination RouteTimeAxisView.
1337 * @param dest_layer Destination layer.
1338 * @param where Destination position.
1339 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1340 * that clear_changes () is only called once per playlist.
1341 * @return New RegionView, or 0 if no insert was performed.
1344 RegionMoveDrag::insert_region_into_playlist (
1345 boost::shared_ptr<Region> region,
1346 RouteTimeAxisView* dest_rtv,
1349 PlaylistSet& modified_playlists
1352 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1353 if (!dest_playlist) {
1357 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1358 _new_region_view = 0;
1359 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1361 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1362 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1364 dest_playlist->clear_changes ();
1367 dest_playlist->add_region (region, where);
1369 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1370 dest_playlist->set_layer (region, dest_layer);
1375 assert (_new_region_view);
1377 return _new_region_view;
1381 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1383 _new_region_view = rv;
1387 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1389 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1390 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1392 _editor->session()->add_command (c);
1401 RegionMoveDrag::aborted (bool movement_occurred)
1405 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1412 RegionMotionDrag::aborted (movement_occurred);
1417 RegionMotionDrag::aborted (bool)
1419 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1421 StreamView* sview = (*i)->view();
1424 if (sview->layer_display() == Expanded) {
1425 sview->set_layer_display (Stacked);
1430 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1431 RegionView* rv = i->view;
1432 TimeAxisView* tv = &(rv->get_time_axis_view ());
1433 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1435 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1436 rv->get_canvas_group()->set_y_position (0);
1438 rv->set_opacity_for_drag (false);
1439 rv->move (-_total_x_delta, 0);
1440 rv->set_height (rtv->view()->child_height ());
1444 /** @param b true to brush, otherwise false.
1445 * @param c true to make copies of the regions being moved, otherwise false.
1447 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1448 : RegionMotionDrag (e, i, p, v, b),
1451 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1454 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1455 if (rtv && rtv->is_track()) {
1456 speed = rtv->track()->speed ();
1459 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1463 RegionMoveDrag::setup_pointer_frame_offset ()
1465 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1468 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1469 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1471 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1473 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1474 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1476 _primary = v->view()->create_region_view (r, false, false);
1478 _primary->get_canvas_group()->show ();
1479 _primary->set_position (pos, 0);
1480 _views.push_back (DraggingView (_primary, this));
1482 _last_frame_position = pos;
1484 _item = _primary->get_canvas_group ();
1488 RegionInsertDrag::finished (GdkEvent *, bool)
1490 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1492 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1493 _primary->get_canvas_group()->set_y_position (0);
1495 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1497 _editor->begin_reversible_command (Operations::insert_region);
1498 playlist->clear_changes ();
1499 playlist->add_region (_primary->region (), _last_frame_position);
1500 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1501 _editor->commit_reversible_command ();
1509 RegionInsertDrag::aborted (bool)
1516 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1517 : RegionMoveDrag (e, i, p, v, false, false)
1519 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1522 struct RegionSelectionByPosition {
1523 bool operator() (RegionView*a, RegionView* b) {
1524 return a->region()->position () < b->region()->position();
1529 RegionSpliceDrag::motion (GdkEvent* event, bool)
1531 /* Which trackview is this ? */
1533 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1534 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1536 /* The region motion is only processed if the pointer is over
1540 if (!tv || !tv->is_track()) {
1541 /* To make sure we hide the verbose canvas cursor when the mouse is
1542 not held over and audiotrack.
1544 _editor->verbose_cursor()->hide ();
1550 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1556 RegionSelection copy (_editor->selection->regions);
1558 RegionSelectionByPosition cmp;
1561 framepos_t const pf = adjusted_current_frame (event);
1563 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1565 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1571 boost::shared_ptr<Playlist> playlist;
1573 if ((playlist = atv->playlist()) == 0) {
1577 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1582 if (pf < (*i)->region()->last_frame() + 1) {
1586 if (pf > (*i)->region()->first_frame()) {
1592 playlist->shuffle ((*i)->region(), dir);
1597 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1599 RegionMoveDrag::finished (event, movement_occurred);
1603 RegionSpliceDrag::aborted (bool)
1608 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1610 _view (dynamic_cast<MidiTimeAxisView*> (v))
1612 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1618 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1621 _region = add_midi_region (_view);
1622 _view->playlist()->freeze ();
1625 framepos_t const f = adjusted_current_frame (event);
1626 if (f < grab_frame()) {
1627 _region->set_position (f);
1630 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1631 so that if this region is duplicated, its duplicate starts on
1632 a snap point rather than 1 frame after a snap point. Otherwise things get
1633 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1634 place snapped notes at the start of the region.
1637 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1638 _region->set_length (len < 1 ? 1 : len);
1644 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1646 if (!movement_occurred) {
1647 add_midi_region (_view);
1649 _view->playlist()->thaw ();
1654 RegionCreateDrag::aborted (bool)
1657 _view->playlist()->thaw ();
1663 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1667 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1671 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1673 Gdk::Cursor* cursor;
1674 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1676 float x_fraction = cnote->mouse_x_fraction ();
1678 if (x_fraction > 0.0 && x_fraction < 0.25) {
1679 cursor = _editor->cursors()->left_side_trim;
1681 cursor = _editor->cursors()->right_side_trim;
1684 Drag::start_grab (event, cursor);
1686 region = &cnote->region_view();
1688 double const region_start = region->get_position_pixels();
1689 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1691 if (grab_x() <= middle_point) {
1692 cursor = _editor->cursors()->left_side_trim;
1695 cursor = _editor->cursors()->right_side_trim;
1701 if (event->motion.state & Keyboard::PrimaryModifier) {
1707 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1709 if (ms.size() > 1) {
1710 /* has to be relative, may make no sense otherwise */
1714 /* select this note; if it is already selected, preserve the existing selection,
1715 otherwise make this note the only one selected.
1717 region->note_selected (cnote, cnote->selected ());
1719 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1720 MidiRegionSelection::iterator next;
1723 (*r)->begin_resizing (at_front);
1729 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1731 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1732 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1733 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1735 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1740 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1742 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1743 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1744 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1746 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1751 NoteResizeDrag::aborted (bool)
1753 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1754 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1755 (*r)->abort_resizing ();
1759 AVDraggingView::AVDraggingView (RegionView* v)
1762 initial_position = v->region()->position ();
1765 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1768 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1771 TrackViewList empty;
1773 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1774 std::list<RegionView*> views = rs.by_layer();
1776 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1777 RegionView* rv = (*i);
1778 if (!rv->region()->video_locked()) {
1781 _views.push_back (AVDraggingView (rv));
1786 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1788 Drag::start_grab (event);
1789 if (_editor->session() == 0) {
1793 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1794 _max_backwards_drag = (
1795 ARDOUR_UI::instance()->video_timeline->get_duration()
1796 + ARDOUR_UI::instance()->video_timeline->get_offset()
1797 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1800 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1801 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1802 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1805 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1808 Timecode::Time timecode;
1809 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1810 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);
1811 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1812 _editor->verbose_cursor()->show ();
1816 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1818 if (_editor->session() == 0) {
1821 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1825 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1826 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
1828 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1829 dt = - _max_backwards_drag;
1832 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1833 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1835 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1836 RegionView* rv = i->view;
1837 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1840 rv->set_opacity_for_drag (true);
1841 rv->region()->clear_changes ();
1842 rv->region()->suspend_property_changes();
1844 rv->region()->set_position(i->initial_position + dt);
1845 rv->region_changed(ARDOUR::Properties::position);
1848 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1849 Timecode::Time timecode;
1850 Timecode::Time timediff;
1852 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1853 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1854 snprintf (buf, sizeof (buf),
1855 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1856 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1857 , _("Video Start:"),
1858 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1860 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1862 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1863 _editor->verbose_cursor()->show ();
1867 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1869 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1873 if (!movement_occurred || ! _editor->session()) {
1877 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1879 _editor->begin_reversible_command (_("Move Video"));
1881 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1882 ARDOUR_UI::instance()->video_timeline->save_undo();
1883 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1884 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1886 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1887 i->view->drag_end();
1888 i->view->set_opacity_for_drag (false);
1889 i->view->region()->resume_property_changes ();
1891 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1894 _editor->session()->maybe_update_session_range(
1895 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1896 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1900 _editor->commit_reversible_command ();
1904 VideoTimeLineDrag::aborted (bool)
1906 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1909 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1910 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1912 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1913 i->view->region()->resume_property_changes ();
1914 i->view->region()->set_position(i->initial_position);
1918 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1919 : RegionDrag (e, i, p, v)
1920 , _preserve_fade_anchor (preserve_fade_anchor)
1921 , _jump_position_when_done (false)
1923 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1927 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1930 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1931 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1933 if (tv && tv->is_track()) {
1934 speed = tv->track()->speed();
1937 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1938 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1939 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1941 framepos_t const pf = adjusted_current_frame (event);
1943 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1944 /* Move the contents of the region around without changing the region bounds */
1945 _operation = ContentsTrim;
1946 Drag::start_grab (event, _editor->cursors()->trimmer);
1948 /* These will get overridden for a point trim.*/
1949 if (pf < (region_start + region_length/2)) {
1950 /* closer to front */
1951 _operation = StartTrim;
1952 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1955 _operation = EndTrim;
1956 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1960 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1961 _jump_position_when_done = true;
1964 switch (_operation) {
1966 show_verbose_cursor_time (region_start);
1967 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1968 i->view->trim_front_starting ();
1972 show_verbose_cursor_time (region_end);
1975 show_verbose_cursor_time (pf);
1979 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1980 i->view->region()->suspend_property_changes ();
1985 TrimDrag::motion (GdkEvent* event, bool first_move)
1987 RegionView* rv = _primary;
1990 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1991 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1992 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1993 frameoffset_t frame_delta = 0;
1995 if (tv && tv->is_track()) {
1996 speed = tv->track()->speed();
1999 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2005 switch (_operation) {
2007 trim_type = "Region start trim";
2010 trim_type = "Region end trim";
2013 trim_type = "Region content trim";
2020 _editor->begin_reversible_command (trim_type);
2022 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2023 RegionView* rv = i->view;
2024 rv->set_opacity_for_drag (false);
2025 rv->enable_display (false);
2026 rv->region()->playlist()->clear_owned_changes ();
2028 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2031 arv->temporarily_hide_envelope ();
2035 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2036 insert_result = _editor->motion_frozen_playlists.insert (pl);
2038 if (insert_result.second) {
2044 bool non_overlap_trim = false;
2046 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2047 non_overlap_trim = true;
2050 /* contstrain trim to fade length */
2051 if (_preserve_fade_anchor) {
2052 switch (_operation) {
2054 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2055 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2057 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2058 if (ar->locked()) continue;
2059 framecnt_t len = ar->fade_in()->back()->when;
2060 if (len < dt) dt = min(dt, len);
2064 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2065 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2067 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2068 if (ar->locked()) continue;
2069 framecnt_t len = ar->fade_out()->back()->when;
2070 if (len < -dt) dt = max(dt, -len);
2079 switch (_operation) {
2081 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2082 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2083 if (changed && _preserve_fade_anchor) {
2084 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2086 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2087 framecnt_t len = ar->fade_in()->back()->when;
2088 framecnt_t diff = ar->first_frame() - i->initial_position;
2089 framepos_t new_length = len - diff;
2090 i->anchored_fade_length = min (ar->length(), new_length);
2091 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2092 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2099 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2100 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2101 if (changed && _preserve_fade_anchor) {
2102 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2104 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2105 framecnt_t len = ar->fade_out()->back()->when;
2106 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2107 framepos_t new_length = len + diff;
2108 i->anchored_fade_length = min (ar->length(), new_length);
2109 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2110 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2118 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2120 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2121 i->view->move_contents (frame_delta);
2127 switch (_operation) {
2129 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2132 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2135 // show_verbose_cursor_time (frame_delta);
2142 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2144 if (movement_occurred) {
2145 motion (event, false);
2147 if (_operation == StartTrim) {
2148 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2150 /* This must happen before the region's StatefulDiffCommand is created, as it may
2151 `correct' (ahem) the region's _start from being negative to being zero. It
2152 needs to be zero in the undo record.
2154 i->view->trim_front_ending ();
2156 if (_preserve_fade_anchor) {
2157 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2159 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2160 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2161 ar->set_fade_in_length(i->anchored_fade_length);
2162 ar->set_fade_in_active(true);
2165 if (_jump_position_when_done) {
2166 i->view->region()->set_position (i->initial_position);
2169 } else if (_operation == EndTrim) {
2170 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2171 if (_preserve_fade_anchor) {
2172 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2174 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2175 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2176 ar->set_fade_out_length(i->anchored_fade_length);
2177 ar->set_fade_out_active(true);
2180 if (_jump_position_when_done) {
2181 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2186 if (!_views.empty()) {
2187 if (_operation == StartTrim) {
2188 _editor->maybe_locate_with_edit_preroll(
2189 _views.begin()->view->region()->position());
2191 if (_operation == EndTrim) {
2192 _editor->maybe_locate_with_edit_preroll(
2193 _views.begin()->view->region()->position() +
2194 _views.begin()->view->region()->length());
2198 if (!_editor->selection->selected (_primary)) {
2199 _primary->thaw_after_trim ();
2202 set<boost::shared_ptr<Playlist> > diffed_playlists;
2204 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2205 i->view->thaw_after_trim ();
2206 i->view->enable_display (true);
2207 i->view->set_opacity_for_drag (true);
2209 /* Trimming one region may affect others on the playlist, so we need
2210 to get undo Commands from the whole playlist rather than just the
2211 region. Use diffed_playlists to make sure we don't diff a given
2212 playlist more than once.
2214 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2215 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2216 vector<Command*> cmds;
2218 _editor->session()->add_commands (cmds);
2219 diffed_playlists.insert (p);
2224 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2228 _editor->motion_frozen_playlists.clear ();
2229 _editor->commit_reversible_command();
2232 /* no mouse movement */
2233 _editor->point_trim (event, adjusted_current_frame (event));
2236 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2237 if (_operation == StartTrim) {
2238 i->view->trim_front_ending ();
2241 i->view->region()->resume_property_changes ();
2246 TrimDrag::aborted (bool movement_occurred)
2248 /* Our motion method is changing model state, so use the Undo system
2249 to cancel. Perhaps not ideal, as this will leave an Undo point
2250 behind which may be slightly odd from the user's point of view.
2255 if (movement_occurred) {
2259 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2260 i->view->region()->resume_property_changes ();
2265 TrimDrag::setup_pointer_frame_offset ()
2267 list<DraggingView>::iterator i = _views.begin ();
2268 while (i != _views.end() && i->view != _primary) {
2272 if (i == _views.end()) {
2276 switch (_operation) {
2278 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2281 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2288 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2292 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2293 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2298 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2300 Drag::start_grab (event, cursor);
2301 show_verbose_cursor_time (adjusted_current_frame(event));
2305 MeterMarkerDrag::setup_pointer_frame_offset ()
2307 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2311 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2313 if (!_marker->meter().movable()) {
2319 // create a dummy marker for visual representation of moving the
2320 // section, because whether its a copy or not, we're going to
2321 // leave or lose the original marker (leave if its a copy; lose if its
2322 // not, because we'll remove it from the map).
2324 MeterSection section (_marker->meter());
2326 if (!section.movable()) {
2331 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2333 _marker = new MeterMarker (
2335 *_editor->meter_group,
2336 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2338 *new MeterSection (_marker->meter())
2341 /* use the new marker for the grab */
2342 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2345 TempoMap& map (_editor->session()->tempo_map());
2346 /* get current state */
2347 before_state = &map.get_state();
2348 /* remove the section while we drag it */
2349 map.remove_meter (section, true);
2353 framepos_t const pf = adjusted_current_frame (event);
2354 _marker->set_position (pf);
2355 show_verbose_cursor_time (pf);
2359 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2361 if (!movement_occurred) {
2362 if (was_double_click()) {
2363 _editor->edit_meter_marker (*_marker);
2368 if (!_marker->meter().movable()) {
2372 motion (event, false);
2374 Timecode::BBT_Time when;
2376 TempoMap& map (_editor->session()->tempo_map());
2377 map.bbt_time (last_pointer_frame(), when);
2379 if (_copy == true) {
2380 _editor->begin_reversible_command (_("copy meter mark"));
2381 XMLNode &before = map.get_state();
2382 map.add_meter (_marker->meter(), when);
2383 XMLNode &after = map.get_state();
2384 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2385 _editor->commit_reversible_command ();
2388 _editor->begin_reversible_command (_("move meter mark"));
2390 /* we removed it before, so add it back now */
2392 map.add_meter (_marker->meter(), when);
2393 XMLNode &after = map.get_state();
2394 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2395 _editor->commit_reversible_command ();
2398 // delete the dummy marker we used for visual representation while moving.
2399 // a new visual marker will show up automatically.
2404 MeterMarkerDrag::aborted (bool moved)
2406 _marker->set_position (_marker->meter().frame ());
2409 TempoMap& map (_editor->session()->tempo_map());
2410 /* we removed it before, so add it back now */
2411 map.add_meter (_marker->meter(), _marker->meter().frame());
2412 // delete the dummy marker we used for visual representation while moving.
2413 // a new visual marker will show up automatically.
2418 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2422 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2424 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2429 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2431 Drag::start_grab (event, cursor);
2432 show_verbose_cursor_time (adjusted_current_frame (event));
2436 TempoMarkerDrag::setup_pointer_frame_offset ()
2438 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2442 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2444 if (!_marker->tempo().movable()) {
2450 // create a dummy marker for visual representation of moving the
2451 // section, because whether its a copy or not, we're going to
2452 // leave or lose the original marker (leave if its a copy; lose if its
2453 // not, because we'll remove it from the map).
2455 // create a dummy marker for visual representation of moving the copy.
2456 // The actual copying is not done before we reach the finish callback.
2459 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2461 TempoSection section (_marker->tempo());
2463 _marker = new TempoMarker (
2465 *_editor->tempo_group,
2466 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2468 *new TempoSection (_marker->tempo())
2471 /* use the new marker for the grab */
2472 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2475 TempoMap& map (_editor->session()->tempo_map());
2476 /* get current state */
2477 before_state = &map.get_state();
2478 /* remove the section while we drag it */
2479 map.remove_tempo (section, true);
2483 framepos_t const pf = adjusted_current_frame (event);
2484 _marker->set_position (pf);
2485 show_verbose_cursor_time (pf);
2489 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2491 if (!movement_occurred) {
2492 if (was_double_click()) {
2493 _editor->edit_tempo_marker (*_marker);
2498 if (!_marker->tempo().movable()) {
2502 motion (event, false);
2504 TempoMap& map (_editor->session()->tempo_map());
2505 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2506 Timecode::BBT_Time when;
2508 map.bbt_time (beat_time, when);
2510 if (_copy == true) {
2511 _editor->begin_reversible_command (_("copy tempo mark"));
2512 XMLNode &before = map.get_state();
2513 map.add_tempo (_marker->tempo(), when);
2514 XMLNode &after = map.get_state();
2515 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2516 _editor->commit_reversible_command ();
2519 _editor->begin_reversible_command (_("move tempo mark"));
2520 /* we removed it before, so add it back now */
2521 map.add_tempo (_marker->tempo(), when);
2522 XMLNode &after = map.get_state();
2523 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2524 _editor->commit_reversible_command ();
2527 // delete the dummy marker we used for visual representation while moving.
2528 // a new visual marker will show up automatically.
2533 TempoMarkerDrag::aborted (bool moved)
2535 _marker->set_position (_marker->tempo().frame());
2537 TempoMap& map (_editor->session()->tempo_map());
2538 /* we removed it before, so add it back now */
2539 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2540 // delete the dummy marker we used for visual representation while moving.
2541 // a new visual marker will show up automatically.
2546 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2547 : Drag (e, &c.track_canvas_item(), false)
2551 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2554 /** Do all the things we do when dragging the playhead to make it look as though
2555 * we have located, without actually doing the locate (because that would cause
2556 * the diskstream buffers to be refilled, which is too slow).
2559 CursorDrag::fake_locate (framepos_t t)
2561 _editor->playhead_cursor->set_position (t);
2563 Session* s = _editor->session ();
2564 if (s->timecode_transmission_suspended ()) {
2565 framepos_t const f = _editor->playhead_cursor->current_frame ();
2566 /* This is asynchronous so it will be sent "now"
2568 s->send_mmc_locate (f);
2569 /* These are synchronous and will be sent during the next
2572 s->queue_full_time_code ();
2573 s->queue_song_position_pointer ();
2576 show_verbose_cursor_time (t);
2577 _editor->UpdateAllTransportClocks (t);
2581 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2583 Drag::start_grab (event, c);
2585 _grab_zoom = _editor->samples_per_pixel;
2587 framepos_t where = _editor->canvas_event_sample (event);
2589 _editor->snap_to_with_modifier (where, event);
2591 _editor->_dragging_playhead = true;
2593 Session* s = _editor->session ();
2595 /* grab the track canvas item as well */
2597 _cursor.track_canvas_item().grab();
2600 if (_was_rolling && _stop) {
2604 if (s->is_auditioning()) {
2605 s->cancel_audition ();
2609 if (AudioEngine::instance()->connected()) {
2611 /* do this only if we're the engine is connected
2612 * because otherwise this request will never be
2613 * serviced and we'll busy wait forever. likewise,
2614 * notice if we are disconnected while waiting for the
2615 * request to be serviced.
2618 s->request_suspend_timecode_transmission ();
2619 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2620 /* twiddle our thumbs */
2625 fake_locate (where);
2629 CursorDrag::motion (GdkEvent* event, bool)
2631 framepos_t const adjusted_frame = adjusted_current_frame (event);
2632 if (adjusted_frame != last_pointer_frame()) {
2633 fake_locate (adjusted_frame);
2638 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2640 _editor->_dragging_playhead = false;
2642 _cursor.track_canvas_item().ungrab();
2644 if (!movement_occurred && _stop) {
2648 motion (event, false);
2650 Session* s = _editor->session ();
2652 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2653 _editor->_pending_locate_request = true;
2654 s->request_resume_timecode_transmission ();
2659 CursorDrag::aborted (bool)
2661 _cursor.track_canvas_item().ungrab();
2663 if (_editor->_dragging_playhead) {
2664 _editor->session()->request_resume_timecode_transmission ();
2665 _editor->_dragging_playhead = false;
2668 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2671 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2672 : RegionDrag (e, i, p, v)
2674 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2678 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2680 Drag::start_grab (event, cursor);
2682 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2683 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2685 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2689 FadeInDrag::setup_pointer_frame_offset ()
2691 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2692 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2693 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2697 FadeInDrag::motion (GdkEvent* event, bool)
2699 framecnt_t fade_length;
2701 framepos_t const pos = adjusted_current_frame (event);
2703 boost::shared_ptr<Region> region = _primary->region ();
2705 if (pos < (region->position() + 64)) {
2706 fade_length = 64; // this should be a minimum defined somewhere
2707 } else if (pos > region->last_frame()) {
2708 fade_length = region->length();
2710 fade_length = pos - region->position();
2713 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2715 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2721 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2724 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2728 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2730 if (!movement_occurred) {
2734 framecnt_t fade_length;
2736 framepos_t const pos = adjusted_current_frame (event);
2738 boost::shared_ptr<Region> region = _primary->region ();
2740 if (pos < (region->position() + 64)) {
2741 fade_length = 64; // this should be a minimum defined somewhere
2742 } else if (pos > region->last_frame()) {
2743 fade_length = region->length();
2745 fade_length = pos - region->position();
2748 _editor->begin_reversible_command (_("change fade in length"));
2750 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2752 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2758 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2759 XMLNode &before = alist->get_state();
2761 tmp->audio_region()->set_fade_in_length (fade_length);
2762 tmp->audio_region()->set_fade_in_active (true);
2764 XMLNode &after = alist->get_state();
2765 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2768 _editor->commit_reversible_command ();
2772 FadeInDrag::aborted (bool)
2774 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2775 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2781 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2785 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2786 : RegionDrag (e, i, p, v)
2788 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2792 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2794 Drag::start_grab (event, cursor);
2796 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2797 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2799 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2803 FadeOutDrag::setup_pointer_frame_offset ()
2805 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2806 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2807 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2811 FadeOutDrag::motion (GdkEvent* event, bool)
2813 framecnt_t fade_length;
2815 framepos_t const pos = adjusted_current_frame (event);
2817 boost::shared_ptr<Region> region = _primary->region ();
2819 if (pos > (region->last_frame() - 64)) {
2820 fade_length = 64; // this should really be a minimum fade defined somewhere
2822 else if (pos < region->position()) {
2823 fade_length = region->length();
2826 fade_length = region->last_frame() - pos;
2829 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2831 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2837 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2840 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2844 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2846 if (!movement_occurred) {
2850 framecnt_t fade_length;
2852 framepos_t const pos = adjusted_current_frame (event);
2854 boost::shared_ptr<Region> region = _primary->region ();
2856 if (pos > (region->last_frame() - 64)) {
2857 fade_length = 64; // this should really be a minimum fade defined somewhere
2859 else if (pos < region->position()) {
2860 fade_length = region->length();
2863 fade_length = region->last_frame() - pos;
2866 _editor->begin_reversible_command (_("change fade out length"));
2868 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2870 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2876 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2877 XMLNode &before = alist->get_state();
2879 tmp->audio_region()->set_fade_out_length (fade_length);
2880 tmp->audio_region()->set_fade_out_active (true);
2882 XMLNode &after = alist->get_state();
2883 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2886 _editor->commit_reversible_command ();
2890 FadeOutDrag::aborted (bool)
2892 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2893 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2899 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2903 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2906 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2908 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2911 _points.push_back (ArdourCanvas::Duple (0, 0));
2912 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2915 MarkerDrag::~MarkerDrag ()
2917 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2922 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2924 location = new Location (*l);
2925 markers.push_back (m);
2930 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2932 Drag::start_grab (event, cursor);
2936 Location *location = _editor->find_location_from_marker (_marker, is_start);
2937 _editor->_dragging_edit_point = true;
2939 update_item (location);
2941 // _drag_line->show();
2942 // _line->raise_to_top();
2945 show_verbose_cursor_time (location->start());
2947 show_verbose_cursor_time (location->end());
2950 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2953 case Selection::Toggle:
2954 /* we toggle on the button release */
2956 case Selection::Set:
2957 if (!_editor->selection->selected (_marker)) {
2958 _editor->selection->set (_marker);
2961 case Selection::Extend:
2963 Locations::LocationList ll;
2964 list<Marker*> to_add;
2966 _editor->selection->markers.range (s, e);
2967 s = min (_marker->position(), s);
2968 e = max (_marker->position(), e);
2971 if (e < max_framepos) {
2974 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2975 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2976 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2979 to_add.push_back (lm->start);
2982 to_add.push_back (lm->end);
2986 if (!to_add.empty()) {
2987 _editor->selection->add (to_add);
2991 case Selection::Add:
2992 _editor->selection->add (_marker);
2996 /* Set up copies for us to manipulate during the drag
2999 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3001 Location* l = _editor->find_location_from_marker (*i, is_start);
3008 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3010 /* range: check that the other end of the range isn't
3013 CopiedLocationInfo::iterator x;
3014 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3015 if (*(*x).location == *l) {
3019 if (x == _copied_locations.end()) {
3020 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3022 (*x).markers.push_back (*i);
3023 (*x).move_both = true;
3031 MarkerDrag::setup_pointer_frame_offset ()
3034 Location *location = _editor->find_location_from_marker (_marker, is_start);
3035 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3039 MarkerDrag::motion (GdkEvent* event, bool)
3041 framecnt_t f_delta = 0;
3043 bool move_both = false;
3044 Location *real_location;
3045 Location *copy_location = 0;
3047 framepos_t const newframe = adjusted_current_frame (event);
3048 framepos_t next = newframe;
3050 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3054 CopiedLocationInfo::iterator x;
3056 /* find the marker we're dragging, and compute the delta */
3058 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3060 copy_location = (*x).location;
3062 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3064 /* this marker is represented by this
3065 * CopiedLocationMarkerInfo
3068 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3073 if (real_location->is_mark()) {
3074 f_delta = newframe - copy_location->start();
3078 switch (_marker->type()) {
3079 case Marker::SessionStart:
3080 case Marker::RangeStart:
3081 case Marker::LoopStart:
3082 case Marker::PunchIn:
3083 f_delta = newframe - copy_location->start();
3086 case Marker::SessionEnd:
3087 case Marker::RangeEnd:
3088 case Marker::LoopEnd:
3089 case Marker::PunchOut:
3090 f_delta = newframe - copy_location->end();
3093 /* what kind of marker is this ? */
3102 if (x == _copied_locations.end()) {
3103 /* hmm, impossible - we didn't find the dragged marker */
3107 /* now move them all */
3109 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3111 copy_location = x->location;
3113 /* call this to find out if its the start or end */
3115 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3119 if (real_location->locked()) {
3123 if (copy_location->is_mark()) {
3127 copy_location->set_start (copy_location->start() + f_delta);
3131 framepos_t new_start = copy_location->start() + f_delta;
3132 framepos_t new_end = copy_location->end() + f_delta;
3134 if (is_start) { // start-of-range marker
3136 if (move_both || (*x).move_both) {
3137 copy_location->set_start (new_start);
3138 copy_location->set_end (new_end);
3139 } else if (new_start < copy_location->end()) {
3140 copy_location->set_start (new_start);
3141 } else if (newframe > 0) {
3142 _editor->snap_to (next, 1, true);
3143 copy_location->set_end (next);
3144 copy_location->set_start (newframe);
3147 } else { // end marker
3149 if (move_both || (*x).move_both) {
3150 copy_location->set_end (new_end);
3151 copy_location->set_start (new_start);
3152 } else if (new_end > copy_location->start()) {
3153 copy_location->set_end (new_end);
3154 } else if (newframe > 0) {
3155 _editor->snap_to (next, -1, true);
3156 copy_location->set_start (next);
3157 copy_location->set_end (newframe);
3162 update_item (copy_location);
3164 /* now lookup the actual GUI items used to display this
3165 * location and move them to wherever the copy of the location
3166 * is now. This means that the logic in ARDOUR::Location is
3167 * still enforced, even though we are not (yet) modifying
3168 * the real Location itself.
3171 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3174 lm->set_position (copy_location->start(), copy_location->end());
3179 assert (!_copied_locations.empty());
3181 show_verbose_cursor_time (newframe);
3185 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3187 if (!movement_occurred) {
3189 if (was_double_click()) {
3190 _editor->rename_marker (_marker);
3194 /* just a click, do nothing but finish
3195 off the selection process
3198 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3201 case Selection::Set:
3202 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3203 _editor->selection->set (_marker);
3207 case Selection::Toggle:
3208 /* we toggle on the button release, click only */
3209 _editor->selection->toggle (_marker);
3212 case Selection::Extend:
3213 case Selection::Add:
3220 _editor->_dragging_edit_point = false;
3222 _editor->begin_reversible_command ( _("move marker") );
3223 XMLNode &before = _editor->session()->locations()->get_state();
3225 MarkerSelection::iterator i;
3226 CopiedLocationInfo::iterator x;
3229 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3230 x != _copied_locations.end() && i != _editor->selection->markers.end();
3233 Location * location = _editor->find_location_from_marker (*i, is_start);
3237 if (location->locked()) {
3241 if (location->is_mark()) {
3242 location->set_start (((*x).location)->start());
3244 location->set (((*x).location)->start(), ((*x).location)->end());
3249 XMLNode &after = _editor->session()->locations()->get_state();
3250 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3251 _editor->commit_reversible_command ();
3255 MarkerDrag::aborted (bool)
3261 MarkerDrag::update_item (Location*)
3266 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3268 _cumulative_x_drag (0),
3269 _cumulative_y_drag (0)
3271 if (_zero_gain_fraction < 0.0) {
3272 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3275 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3277 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3283 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3285 Drag::start_grab (event, _editor->cursors()->fader);
3287 // start the grab at the center of the control point so
3288 // the point doesn't 'jump' to the mouse after the first drag
3289 _fixed_grab_x = _point->get_x();
3290 _fixed_grab_y = _point->get_y();
3292 float const fraction = 1 - (_point->get_y() / _point->line().height());
3294 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3296 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3297 event->button.x + 10, event->button.y + 10);
3299 _editor->verbose_cursor()->show ();
3301 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3303 if (!_point->can_slide ()) {
3304 _x_constrained = true;
3309 ControlPointDrag::motion (GdkEvent* event, bool)
3311 double dx = _drags->current_pointer_x() - last_pointer_x();
3312 double dy = current_pointer_y() - last_pointer_y();
3314 if (event->button.state & Keyboard::SecondaryModifier) {
3319 /* coordinate in pixels relative to the start of the region (for region-based automation)
3320 or track (for track-based automation) */
3321 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3322 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3324 // calculate zero crossing point. back off by .01 to stay on the
3325 // positive side of zero
3326 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3328 // make sure we hit zero when passing through
3329 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3333 if (_x_constrained) {
3336 if (_y_constrained) {
3340 _cumulative_x_drag = cx - _fixed_grab_x;
3341 _cumulative_y_drag = cy - _fixed_grab_y;
3345 cy = min ((double) _point->line().height(), cy);
3347 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3349 if (!_x_constrained) {
3350 _editor->snap_to_with_modifier (cx_frames, event);
3353 cx_frames = min (cx_frames, _point->line().maximum_time());
3355 float const fraction = 1.0 - (cy / _point->line().height());
3357 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3359 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3363 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3365 if (!movement_occurred) {
3369 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3370 _editor->reset_point_selection ();
3374 motion (event, false);
3377 _point->line().end_drag (_pushing, _final_index);
3378 _editor->session()->commit_reversible_command ();
3382 ControlPointDrag::aborted (bool)
3384 _point->line().reset ();
3388 ControlPointDrag::active (Editing::MouseMode m)
3390 if (m == Editing::MouseGain) {
3391 /* always active in mouse gain */
3395 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3396 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3399 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3402 _cumulative_y_drag (0)
3404 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3408 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3410 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3413 _item = &_line->grab_item ();
3415 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3416 origin, and ditto for y.
3419 double cx = event->button.x;
3420 double cy = event->button.y;
3422 _line->parent_group().canvas_to_item (cx, cy);
3424 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3429 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3430 /* no adjacent points */
3434 Drag::start_grab (event, _editor->cursors()->fader);
3436 /* store grab start in parent frame */
3441 double fraction = 1.0 - (cy / _line->height());
3443 _line->start_drag_line (before, after, fraction);
3445 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3446 event->button.x + 10, event->button.y + 10);
3448 _editor->verbose_cursor()->show ();
3452 LineDrag::motion (GdkEvent* event, bool)
3454 double dy = current_pointer_y() - last_pointer_y();
3456 if (event->button.state & Keyboard::SecondaryModifier) {
3460 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3462 _cumulative_y_drag = cy - _fixed_grab_y;
3465 cy = min ((double) _line->height(), cy);
3467 double const fraction = 1.0 - (cy / _line->height());
3470 /* we are ignoring x position for this drag, so we can just pass in anything */
3471 _line->drag_motion (0, fraction, true, false, ignored);
3473 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3477 LineDrag::finished (GdkEvent* event, bool movement_occured)
3479 if (movement_occured) {
3480 motion (event, false);
3481 _line->end_drag (false, 0);
3483 /* add a new control point on the line */
3485 AutomationTimeAxisView* atv;
3487 _line->end_drag (false, 0);
3489 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3490 framepos_t where = _editor->window_event_sample (event, 0, 0);
3491 atv->add_automation_event (event, where, event->button.y, false);
3495 _editor->session()->commit_reversible_command ();
3499 LineDrag::aborted (bool)
3504 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3507 _cumulative_x_drag (0)
3509 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3513 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3515 Drag::start_grab (event);
3517 _line = reinterpret_cast<Line*> (_item);
3520 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3522 double cx = event->button.x;
3523 double cy = event->button.y;
3525 _item->parent()->canvas_to_item (cx, cy);
3527 /* store grab start in parent frame */
3528 _region_view_grab_x = cx;
3530 _before = *(float*) _item->get_data ("position");
3532 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3534 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3538 FeatureLineDrag::motion (GdkEvent*, bool)
3540 double dx = _drags->current_pointer_x() - last_pointer_x();
3542 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3544 _cumulative_x_drag += dx;
3546 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3555 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3557 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3559 float *pos = new float;
3562 _line->set_data ("position", pos);
3568 FeatureLineDrag::finished (GdkEvent*, bool)
3570 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3571 _arv->update_transient(_before, _before);
3575 FeatureLineDrag::aborted (bool)
3580 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3582 , _vertical_only (false)
3584 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3588 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3590 Drag::start_grab (event);
3591 show_verbose_cursor_time (adjusted_current_frame (event));
3595 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3602 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3604 framepos_t grab = grab_frame ();
3605 if (Config->get_rubberbanding_snaps_to_grid ()) {
3606 _editor->snap_to_with_modifier (grab, event);
3609 /* base start and end on initial click position */
3619 if (current_pointer_y() < grab_y()) {
3620 y1 = current_pointer_y();
3623 y2 = current_pointer_y();
3627 if (start != end || y1 != y2) {
3629 double x1 = _editor->sample_to_pixel (start);
3630 double x2 = _editor->sample_to_pixel (end);
3631 const double min_dimension = 2.0;
3633 if (_vertical_only) {
3634 /* fixed 10 pixel width */
3638 x2 = min (x1 - min_dimension, x2);
3640 x2 = max (x1 + min_dimension, x2);
3645 y2 = min (y1 - min_dimension, y2);
3647 y2 = max (y1 + min_dimension, y2);
3650 /* translate rect into item space and set */
3652 Rect r (x1, y1, x2, y2);
3654 /* this drag is a _trackview_only == true drag, so the y1 and
3655 * y2 (computed using current_pointer_y() and grab_y()) will be
3656 * relative to the top of the trackview group). The
3657 * rubberband rect has the same parent/scroll offset as the
3658 * the trackview group, so we can use the "r" rect directly
3659 * to set the shape of the rubberband.
3662 _editor->rubberband_rect->set (r);
3663 _editor->rubberband_rect->show();
3664 _editor->rubberband_rect->raise_to_top();
3666 show_verbose_cursor_time (pf);
3668 do_select_things (event, true);
3673 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3678 if (grab_frame() < last_pointer_frame()) {
3680 x2 = last_pointer_frame ();
3683 x1 = last_pointer_frame ();
3689 if (current_pointer_y() < grab_y()) {
3690 y1 = current_pointer_y();
3693 y2 = current_pointer_y();
3697 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3701 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3703 if (movement_occurred) {
3705 motion (event, false);
3706 do_select_things (event, false);
3712 bool do_deselect = true;
3713 MidiTimeAxisView* mtv;
3715 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3717 if (_editor->selection->empty()) {
3718 /* nothing selected */
3719 add_midi_region (mtv);
3720 do_deselect = false;
3724 /* do not deselect if Primary or Tertiary (toggle-select or
3725 * extend-select are pressed.
3728 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3729 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3736 _editor->rubberband_rect->hide();
3740 RubberbandSelectDrag::aborted (bool)
3742 _editor->rubberband_rect->hide ();
3745 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3746 : RegionDrag (e, i, p, v)
3748 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3752 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3754 Drag::start_grab (event, cursor);
3756 show_verbose_cursor_time (adjusted_current_frame (event));
3760 TimeFXDrag::motion (GdkEvent* event, bool)
3762 RegionView* rv = _primary;
3763 StreamView* cv = rv->get_time_axis_view().view ();
3765 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3766 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3767 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3769 framepos_t const pf = adjusted_current_frame (event);
3771 if (pf > rv->region()->position()) {
3772 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3775 show_verbose_cursor_time (pf);
3779 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3781 _primary->get_time_axis_view().hide_timestretch ();
3783 if (!movement_occurred) {
3787 if (last_pointer_frame() < _primary->region()->position()) {
3788 /* backwards drag of the left edge - not usable */
3792 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3794 float percentage = (double) newlen / (double) _primary->region()->length();
3796 #ifndef USE_RUBBERBAND
3797 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3798 if (_primary->region()->data_type() == DataType::AUDIO) {
3799 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3803 if (!_editor->get_selection().regions.empty()) {
3804 /* primary will already be included in the selection, and edit
3805 group shared editing will propagate selection across
3806 equivalent regions, so just use the current region
3810 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3811 error << _("An error occurred while executing time stretch operation") << endmsg;
3817 TimeFXDrag::aborted (bool)
3819 _primary->get_time_axis_view().hide_timestretch ();
3822 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3825 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3829 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3831 Drag::start_grab (event);
3835 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3837 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3841 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3843 if (movement_occurred && _editor->session()) {
3844 /* make sure we stop */
3845 _editor->session()->request_transport_speed (0.0);
3850 ScrubDrag::aborted (bool)
3855 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3860 , _original_pointer_time_axis (-1)
3861 , _last_pointer_time_axis (-1)
3862 , _time_selection_at_start (!_editor->get_selection().time.empty())
3864 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3866 if (_time_selection_at_start) {
3867 start_at_start = _editor->get_selection().time.start();
3868 end_at_start = _editor->get_selection().time.end_frame();
3873 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3875 if (_editor->session() == 0) {
3879 Gdk::Cursor* cursor = 0;
3881 switch (_operation) {
3882 case CreateSelection:
3883 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3888 cursor = _editor->cursors()->selector;
3889 Drag::start_grab (event, cursor);
3892 case SelectionStartTrim:
3893 if (_editor->clicked_axisview) {
3894 _editor->clicked_axisview->order_selection_trims (_item, true);
3896 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3899 case SelectionEndTrim:
3900 if (_editor->clicked_axisview) {
3901 _editor->clicked_axisview->order_selection_trims (_item, false);
3903 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3907 Drag::start_grab (event, cursor);
3910 case SelectionExtend:
3911 Drag::start_grab (event, cursor);
3915 if (_operation == SelectionMove) {
3916 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3918 show_verbose_cursor_time (adjusted_current_frame (event));
3921 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
3925 SelectionDrag::setup_pointer_frame_offset ()
3927 switch (_operation) {
3928 case CreateSelection:
3929 _pointer_frame_offset = 0;
3932 case SelectionStartTrim:
3934 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3937 case SelectionEndTrim:
3938 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3941 case SelectionExtend:
3947 SelectionDrag::motion (GdkEvent* event, bool first_move)
3949 framepos_t start = 0;
3951 framecnt_t length = 0;
3952 framecnt_t distance = 0;
3954 framepos_t const pending_position = adjusted_current_frame (event);
3956 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
3960 switch (_operation) {
3961 case CreateSelection:
3963 framepos_t grab = grab_frame ();
3966 grab = adjusted_current_frame (event, false);
3967 if (grab < pending_position) {
3968 _editor->snap_to (grab, -1);
3970 _editor->snap_to (grab, 1);
3974 if (pending_position < grab) {
3975 start = pending_position;
3978 end = pending_position;
3982 /* first drag: Either add to the selection
3983 or create a new selection
3990 /* adding to the selection */
3991 _editor->set_selected_track_as_side_effect (Selection::Add);
3992 _editor->clicked_selection = _editor->selection->add (start, end);
3999 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4000 _editor->set_selected_track_as_side_effect (Selection::Set);
4003 _editor->clicked_selection = _editor->selection->set (start, end);
4007 /* select all tracks within the rectangle that we've marked out so far */
4008 TrackViewList to_be_added_to_selection;
4009 TrackViewList to_be_removed_from_selection;
4010 TrackViewList& all_tracks (_editor->track_views);
4012 ArdourCanvas::Coord const top = grab_y();
4013 ArdourCanvas::Coord const bottom = current_pointer_y();
4015 if (top >= 0 && bottom >= 0) {
4017 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4019 if ((*i)->covered_by_y_range (top, bottom)) {
4020 if (!(*i)->get_selected()) {
4021 to_be_added_to_selection.push_back (*i);
4024 if ((*i)->get_selected()) {
4025 to_be_removed_from_selection.push_back (*i);
4030 if (!to_be_added_to_selection.empty()) {
4031 _editor->selection->add (to_be_added_to_selection);
4034 if (!to_be_removed_from_selection.empty()) {
4035 _editor->selection->remove (to_be_removed_from_selection);
4041 case SelectionStartTrim:
4043 start = _editor->selection->time[_editor->clicked_selection].start;
4044 end = _editor->selection->time[_editor->clicked_selection].end;
4046 if (pending_position > end) {
4049 start = pending_position;
4053 case SelectionEndTrim:
4055 start = _editor->selection->time[_editor->clicked_selection].start;
4056 end = _editor->selection->time[_editor->clicked_selection].end;
4058 if (pending_position < start) {
4061 end = pending_position;
4068 start = _editor->selection->time[_editor->clicked_selection].start;
4069 end = _editor->selection->time[_editor->clicked_selection].end;
4071 length = end - start;
4072 distance = pending_position - start;
4073 start = pending_position;
4074 _editor->snap_to (start);
4076 end = start + length;
4080 case SelectionExtend:
4085 switch (_operation) {
4087 if (_time_selection_at_start) {
4088 _editor->selection->move_time (distance);
4092 _editor->selection->replace (_editor->clicked_selection, start, end);
4096 if (_operation == SelectionMove) {
4097 show_verbose_cursor_time(start);
4099 show_verbose_cursor_time(pending_position);
4104 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4106 Session* s = _editor->session();
4108 if (movement_occurred) {
4109 motion (event, false);
4110 /* XXX this is not object-oriented programming at all. ick */
4111 if (_editor->selection->time.consolidate()) {
4112 _editor->selection->TimeChanged ();
4115 /* XXX what if its a music time selection? */
4117 if ( s->get_play_range() && s->transport_rolling() ) {
4118 s->request_play_range (&_editor->selection->time, true);
4120 if (Config->get_always_play_range() && !s->transport_rolling()) {
4121 s->request_locate (_editor->get_selection().time.start());
4127 /* just a click, no pointer movement.
4130 if (_operation == SelectionExtend) {
4131 if (_time_selection_at_start) {
4132 framepos_t pos = adjusted_current_frame (event, false);
4133 framepos_t start = min (pos, start_at_start);
4134 framepos_t end = max (pos, end_at_start);
4135 _editor->selection->set (start, end);
4138 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4139 if (_editor->clicked_selection) {
4140 _editor->selection->remove (_editor->clicked_selection);
4143 if (!_editor->clicked_selection) {
4144 _editor->selection->clear_time();
4149 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4150 _editor->selection->set (_editor->clicked_axisview);
4153 if (s && s->get_play_range () && s->transport_rolling()) {
4154 s->request_stop (false, false);
4159 _editor->stop_canvas_autoscroll ();
4160 _editor->clicked_selection = 0;
4164 SelectionDrag::aborted (bool)
4169 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4170 : Drag (e, i, false),
4174 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4176 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4177 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4178 physical_screen_height (_editor->get_window())));
4179 _drag_rect->hide ();
4181 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4182 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4186 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4188 if (_editor->session() == 0) {
4192 Gdk::Cursor* cursor = 0;
4194 if (!_editor->temp_location) {
4195 _editor->temp_location = new Location (*_editor->session());
4198 switch (_operation) {
4199 case CreateRangeMarker:
4200 case CreateTransportMarker:
4201 case CreateCDMarker:
4203 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4208 cursor = _editor->cursors()->selector;
4212 Drag::start_grab (event, cursor);
4214 show_verbose_cursor_time (adjusted_current_frame (event));
4218 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4220 framepos_t start = 0;
4222 ArdourCanvas::Rectangle *crect;
4224 switch (_operation) {
4225 case CreateRangeMarker:
4226 crect = _editor->range_bar_drag_rect;
4228 case CreateTransportMarker:
4229 crect = _editor->transport_bar_drag_rect;
4231 case CreateCDMarker:
4232 crect = _editor->cd_marker_bar_drag_rect;
4235 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4240 framepos_t const pf = adjusted_current_frame (event);
4242 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4243 framepos_t grab = grab_frame ();
4244 _editor->snap_to (grab);
4246 if (pf < grab_frame()) {
4254 /* first drag: Either add to the selection
4255 or create a new selection.
4260 _editor->temp_location->set (start, end);
4264 update_item (_editor->temp_location);
4266 //_drag_rect->raise_to_top();
4272 _editor->temp_location->set (start, end);
4274 double x1 = _editor->sample_to_pixel (start);
4275 double x2 = _editor->sample_to_pixel (end);
4279 update_item (_editor->temp_location);
4282 show_verbose_cursor_time (pf);
4287 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4289 Location * newloc = 0;
4293 if (movement_occurred) {
4294 motion (event, false);
4297 switch (_operation) {
4298 case CreateRangeMarker:
4299 case CreateCDMarker:
4301 _editor->begin_reversible_command (_("new range marker"));
4302 XMLNode &before = _editor->session()->locations()->get_state();
4303 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4304 if (_operation == CreateCDMarker) {
4305 flags = Location::IsRangeMarker | Location::IsCDMarker;
4306 _editor->cd_marker_bar_drag_rect->hide();
4309 flags = Location::IsRangeMarker;
4310 _editor->range_bar_drag_rect->hide();
4312 newloc = new Location (
4313 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4316 _editor->session()->locations()->add (newloc, true);
4317 XMLNode &after = _editor->session()->locations()->get_state();
4318 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4319 _editor->commit_reversible_command ();
4323 case CreateTransportMarker:
4324 // popup menu to pick loop or punch
4325 _editor->new_transport_marker_context_menu (&event->button, _item);
4331 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4333 if (_operation == CreateTransportMarker) {
4335 /* didn't drag, so just locate */
4337 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4339 } else if (_operation == CreateCDMarker) {
4341 /* didn't drag, but mark is already created so do
4344 } else { /* operation == CreateRangeMarker */
4350 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4352 if (end == max_framepos) {
4353 end = _editor->session()->current_end_frame ();
4356 if (start == max_framepos) {
4357 start = _editor->session()->current_start_frame ();
4360 switch (_editor->mouse_mode) {
4362 /* find the two markers on either side and then make the selection from it */
4363 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4367 /* find the two markers on either side of the click and make the range out of it */
4368 _editor->selection->set (start, end);
4377 _editor->stop_canvas_autoscroll ();
4381 RangeMarkerBarDrag::aborted (bool)
4387 RangeMarkerBarDrag::update_item (Location* location)
4389 double const x1 = _editor->sample_to_pixel (location->start());
4390 double const x2 = _editor->sample_to_pixel (location->end());
4392 _drag_rect->set_x0 (x1);
4393 _drag_rect->set_x1 (x2);
4396 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4400 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4404 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4406 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4407 Drag::start_grab (event, _editor->cursors()->zoom_out);
4410 Drag::start_grab (event, _editor->cursors()->zoom_in);
4414 show_verbose_cursor_time (adjusted_current_frame (event));
4418 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4423 framepos_t const pf = adjusted_current_frame (event);
4425 framepos_t grab = grab_frame ();
4426 _editor->snap_to_with_modifier (grab, event);
4428 /* base start and end on initial click position */
4440 _editor->zoom_rect->show();
4441 _editor->zoom_rect->raise_to_top();
4444 _editor->reposition_zoom_rect(start, end);
4446 show_verbose_cursor_time (pf);
4451 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4453 if (movement_occurred) {
4454 motion (event, false);
4456 if (grab_frame() < last_pointer_frame()) {
4457 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4459 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4462 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4463 _editor->tav_zoom_step (_zoom_out);
4465 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4469 _editor->zoom_rect->hide();
4473 MouseZoomDrag::aborted (bool)
4475 _editor->zoom_rect->hide ();
4478 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4480 , _cumulative_dx (0)
4481 , _cumulative_dy (0)
4483 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4485 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4487 _region = &_primary->region_view ();
4488 _note_height = _region->midi_stream_view()->note_height ();
4492 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4494 Drag::start_grab (event);
4496 if (!(_was_selected = _primary->selected())) {
4498 /* tertiary-click means extend selection - we'll do that on button release,
4499 so don't add it here, because otherwise we make it hard to figure
4500 out the "extend-to" range.
4503 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4506 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4509 _region->note_selected (_primary, true);
4511 _region->unique_select (_primary);
4517 /** @return Current total drag x change in frames */
4519 NoteDrag::total_dx () const
4522 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4524 /* primary note time */
4525 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4527 /* new time of the primary note in session frames */
4528 frameoffset_t st = n + dx;
4530 framepos_t const rp = _region->region()->position ();
4532 /* prevent the note being dragged earlier than the region's position */
4535 /* snap and return corresponding delta */
4536 return _region->snap_frame_to_frame (st - rp) + rp - n;
4539 /** @return Current total drag y change in note number */
4541 NoteDrag::total_dy () const
4543 MidiStreamView* msv = _region->midi_stream_view ();
4544 double const y = _region->midi_view()->y_position ();
4545 /* new current note */
4546 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4548 n = max (msv->lowest_note(), n);
4549 n = min (msv->highest_note(), n);
4550 /* and work out delta */
4551 return n - msv->y_to_note (grab_y() - y);
4555 NoteDrag::motion (GdkEvent *, bool)
4557 /* Total change in x and y since the start of the drag */
4558 frameoffset_t const dx = total_dx ();
4559 int8_t const dy = total_dy ();
4561 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4562 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4563 double const tdy = -dy * _note_height - _cumulative_dy;
4566 _cumulative_dx += tdx;
4567 _cumulative_dy += tdy;
4569 int8_t note_delta = total_dy();
4571 _region->move_selection (tdx, tdy, note_delta);
4573 /* the new note value may be the same as the old one, but we
4574 * don't know what that means because the selection may have
4575 * involved more than one note and we might be doing something
4576 * odd with them. so show the note value anyway, always.
4580 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4582 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4583 (int) floor ((double)new_note));
4585 show_verbose_cursor_text (buf);
4590 NoteDrag::finished (GdkEvent* ev, bool moved)
4593 /* no motion - select note */
4595 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4596 _editor->current_mouse_mode() == Editing::MouseDraw) {
4598 if (_was_selected) {
4599 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4601 _region->note_deselected (_primary);
4604 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4605 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4607 if (!extend && !add && _region->selection_size() > 1) {
4608 _region->unique_select (_primary);
4609 } else if (extend) {
4610 _region->note_selected (_primary, true, true);
4612 /* it was added during button press */
4617 _region->note_dropped (_primary, total_dx(), total_dy());
4622 NoteDrag::aborted (bool)
4627 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4628 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4629 : Drag (editor, atv->base_item ())
4631 , _nothing_to_drag (false)
4633 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4634 y_origin = atv->y_position();
4635 setup (atv->lines ());
4638 /** Make an AutomationRangeDrag for region gain lines */
4639 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4640 : Drag (editor, rv->get_canvas_group ())
4642 , _nothing_to_drag (false)
4644 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4646 list<boost::shared_ptr<AutomationLine> > lines;
4647 lines.push_back (rv->get_gain_line ());
4648 y_origin = rv->get_time_axis_view().y_position();
4652 /** @param lines AutomationLines to drag.
4653 * @param offset Offset from the session start to the points in the AutomationLines.
4656 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4658 /* find the lines that overlap the ranges being dragged */
4659 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4660 while (i != lines.end ()) {
4661 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4664 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4666 /* check this range against all the AudioRanges that we are using */
4667 list<AudioRange>::const_iterator k = _ranges.begin ();
4668 while (k != _ranges.end()) {
4669 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4675 /* add it to our list if it overlaps at all */
4676 if (k != _ranges.end()) {
4681 _lines.push_back (n);
4687 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4691 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4693 return 1.0 - ((global_y - y_origin) / line->height());
4697 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4699 Drag::start_grab (event, cursor);
4701 /* Get line states before we start changing things */
4702 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4703 i->state = &i->line->get_state ();
4704 i->original_fraction = y_fraction (i->line, current_pointer_y());
4707 if (_ranges.empty()) {
4709 /* No selected time ranges: drag all points */
4710 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4711 uint32_t const N = i->line->npoints ();
4712 for (uint32_t j = 0; j < N; ++j) {
4713 i->points.push_back (i->line->nth (j));
4719 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4721 framecnt_t const half = (i->start + i->end) / 2;
4723 /* find the line that this audio range starts in */
4724 list<Line>::iterator j = _lines.begin();
4725 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4729 if (j != _lines.end()) {
4730 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4732 /* j is the line that this audio range starts in; fade into it;
4733 64 samples length plucked out of thin air.
4736 framepos_t a = i->start + 64;
4741 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4742 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4744 the_list->add (p, the_list->eval (p));
4745 the_list->add (q, the_list->eval (q));
4748 /* same thing for the end */
4751 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4755 if (j != _lines.end()) {
4756 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4758 /* j is the line that this audio range starts in; fade out of it;
4759 64 samples length plucked out of thin air.
4762 framepos_t b = i->end - 64;
4767 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4768 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4770 the_list->add (p, the_list->eval (p));
4771 the_list->add (q, the_list->eval (q));
4775 _nothing_to_drag = true;
4777 /* Find all the points that should be dragged and put them in the relevant
4778 points lists in the Line structs.
4781 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4783 uint32_t const N = i->line->npoints ();
4784 for (uint32_t j = 0; j < N; ++j) {
4786 /* here's a control point on this line */
4787 ControlPoint* p = i->line->nth (j);
4788 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4790 /* see if it's inside a range */
4791 list<AudioRange>::const_iterator k = _ranges.begin ();
4792 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4796 if (k != _ranges.end()) {
4797 /* dragging this point */
4798 _nothing_to_drag = false;
4799 i->points.push_back (p);
4805 if (_nothing_to_drag) {
4809 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4810 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
4815 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4817 if (_nothing_to_drag) {
4821 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4822 float const f = y_fraction (l->line, current_pointer_y());
4823 /* we are ignoring x position for this drag, so we can just pass in anything */
4825 l->line->drag_motion (0, f, true, false, ignored);
4826 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4831 AutomationRangeDrag::finished (GdkEvent* event, bool)
4833 if (_nothing_to_drag) {
4837 motion (event, false);
4838 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4839 i->line->end_drag (false, 0);
4842 _editor->session()->commit_reversible_command ();
4846 AutomationRangeDrag::aborted (bool)
4848 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4853 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4856 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4857 layer = v->region()->layer ();
4858 initial_y = v->get_canvas_group()->position().y;
4859 initial_playlist = v->region()->playlist ();
4860 initial_position = v->region()->position ();
4861 initial_end = v->region()->position () + v->region()->length ();
4864 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4865 : Drag (e, i->canvas_item ())
4868 , _cumulative_dx (0)
4870 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4871 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4876 PatchChangeDrag::motion (GdkEvent* ev, bool)
4878 framepos_t f = adjusted_current_frame (ev);
4879 boost::shared_ptr<Region> r = _region_view->region ();
4880 f = max (f, r->position ());
4881 f = min (f, r->last_frame ());
4883 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4884 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4885 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4886 _cumulative_dx = dxu;
4890 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4892 if (!movement_occurred) {
4896 boost::shared_ptr<Region> r (_region_view->region ());
4897 framepos_t f = adjusted_current_frame (ev);
4898 f = max (f, r->position ());
4899 f = min (f, r->last_frame ());
4901 _region_view->move_patch_change (
4903 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4908 PatchChangeDrag::aborted (bool)
4910 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4914 PatchChangeDrag::setup_pointer_frame_offset ()
4916 boost::shared_ptr<Region> region = _region_view->region ();
4917 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4920 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4921 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4928 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4930 framepos_t const p = _region_view->region()->position ();
4931 double const y = _region_view->midi_view()->y_position ();
4933 x1 = max ((framepos_t) 0, x1 - p);
4934 x2 = max ((framepos_t) 0, x2 - p);
4935 y1 = max (0.0, y1 - y);
4936 y2 = max (0.0, y2 - y);
4938 _region_view->update_drag_selection (
4939 _editor->sample_to_pixel (x1),
4940 _editor->sample_to_pixel (x2),
4943 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4948 MidiRubberbandSelectDrag::deselect_things ()
4953 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4954 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4957 _vertical_only = true;
4961 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4963 double const y = _region_view->midi_view()->y_position ();
4965 y1 = max (0.0, y1 - y);
4966 y2 = max (0.0, y2 - y);
4968 _region_view->update_vertical_drag_selection (
4971 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4976 MidiVerticalSelectDrag::deselect_things ()
4981 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4982 : RubberbandSelectDrag (e, i)
4988 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4990 if (drag_in_progress) {
4991 /* We just want to select things at the end of the drag, not during it */
4995 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4997 _editor->begin_reversible_command (_("rubberband selection"));
4998 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4999 _editor->commit_reversible_command ();
5003 EditorRubberbandSelectDrag::deselect_things ()
5005 if (!getenv("ARDOUR_SAE")) {
5006 _editor->selection->clear_tracks();
5008 _editor->selection->clear_regions();
5009 _editor->selection->clear_points ();
5010 _editor->selection->clear_lines ();
5013 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5021 NoteCreateDrag::~NoteCreateDrag ()
5027 NoteCreateDrag::grid_frames (framepos_t t) const
5030 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
5035 return _region_view->region_beats_to_region_frames (grid_beats);
5039 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5041 Drag::start_grab (event, cursor);
5043 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5045 framepos_t pf = _drags->current_pointer_frame ();
5046 framecnt_t const g = grid_frames (pf);
5048 /* Hack so that we always snap to the note that we are over, instead of snapping
5049 to the next one if we're more than halfway through the one we're over.
5051 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5055 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5057 MidiStreamView* sv = _region_view->midi_stream_view ();
5058 double const x = _editor->sample_to_pixel (_note[0]);
5059 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5061 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5062 _drag_rect->set_outline_all ();
5063 _drag_rect->set_outline_color (0xffffff99);
5064 _drag_rect->set_fill_color (0xffffff66);
5068 NoteCreateDrag::motion (GdkEvent* event, bool)
5070 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5071 double const x = _editor->sample_to_pixel (_note[1]);
5072 if (_note[1] > _note[0]) {
5073 _drag_rect->set_x1 (x);
5075 _drag_rect->set_x0 (x);
5080 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5082 if (!had_movement) {
5086 framepos_t const start = min (_note[0], _note[1]);
5087 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5089 framecnt_t const g = grid_frames (start);
5090 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5092 if (_editor->snap_mode() == SnapNormal && length < g) {
5093 length = g - one_tick;
5096 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5098 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5102 NoteCreateDrag::y_to_region (double y) const
5105 _region_view->get_canvas_group()->canvas_to_item (x, y);
5110 NoteCreateDrag::aborted (bool)
5115 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5120 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5124 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5126 Drag::start_grab (event, cursor);
5130 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5136 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5139 distance = _drags->current_pointer_x() - grab_x();
5140 len = ar->fade_in()->back()->when;
5142 distance = grab_x() - _drags->current_pointer_x();
5143 len = ar->fade_out()->back()->when;
5146 /* how long should it be ? */
5148 new_length = len + _editor->pixel_to_sample (distance);
5150 /* now check with the region that this is legal */
5152 new_length = ar->verify_xfade_bounds (new_length, start);
5155 arv->reset_fade_in_shape_width (ar, new_length);
5157 arv->reset_fade_out_shape_width (ar, new_length);
5162 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5168 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5171 distance = _drags->current_pointer_x() - grab_x();
5172 len = ar->fade_in()->back()->when;
5174 distance = grab_x() - _drags->current_pointer_x();
5175 len = ar->fade_out()->back()->when;
5178 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5180 _editor->begin_reversible_command ("xfade trim");
5181 ar->playlist()->clear_owned_changes ();
5184 ar->set_fade_in_length (new_length);
5186 ar->set_fade_out_length (new_length);
5189 /* Adjusting the xfade may affect other regions in the playlist, so we need
5190 to get undo Commands from the whole playlist rather than just the
5194 vector<Command*> cmds;
5195 ar->playlist()->rdiff (cmds);
5196 _editor->session()->add_commands (cmds);
5197 _editor->commit_reversible_command ();
5202 CrossfadeEdgeDrag::aborted (bool)
5205 arv->redraw_start_xfade ();
5207 arv->redraw_end_xfade ();