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 /* calling this implies that we expect the event to have canvas
183 * Can we guarantee that this is true?
186 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
188 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
189 bool const t = (*i)->motion_handler (e, from_autoscroll);
190 /* run all handlers; return true if at least one of them
191 returns true (indicating that the event has been handled).
203 DragManager::have_item (ArdourCanvas::Item* i) const
205 list<Drag*>::const_iterator j = _drags.begin ();
206 while (j != _drags.end() && (*j)->item () != i) {
210 return j != _drags.end ();
213 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
216 , _pointer_frame_offset (0)
217 , _trackview_only (trackview_only)
218 , _move_threshold_passed (false)
219 , _was_double_click (false)
220 , _raw_grab_frame (0)
222 , _last_pointer_frame (0)
228 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
241 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
243 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
245 if (Keyboard::is_button2_event (&event->button)) {
246 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
247 _y_constrained = true;
248 _x_constrained = false;
250 _y_constrained = false;
251 _x_constrained = true;
254 _x_constrained = false;
255 _y_constrained = false;
258 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
259 setup_pointer_frame_offset ();
260 _grab_frame = adjusted_frame (_raw_grab_frame, event);
261 _last_pointer_frame = _grab_frame;
262 _last_pointer_x = _grab_x;
264 if (_trackview_only) {
265 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
268 _last_pointer_y = _grab_y;
273 /* CAIROCANVAS need a variant here that passes *cursor */
275 _editor->push_canvas_cursor (cursor);
278 if (_editor->session() && _editor->session()->transport_rolling()) {
281 _was_rolling = false;
284 switch (_editor->snap_type()) {
285 case SnapToRegionStart:
286 case SnapToRegionEnd:
287 case SnapToRegionSync:
288 case SnapToRegionBoundary:
289 _editor->build_region_boundary_cache ();
296 /** Call to end a drag `successfully'. Ungrabs item and calls
297 * subclass' finished() method.
299 * @param event GDK event, or 0.
300 * @return true if some movement occurred, otherwise false.
303 Drag::end_grab (GdkEvent* event)
305 _editor->stop_canvas_autoscroll ();
309 finished (event, _move_threshold_passed);
311 _editor->verbose_cursor()->hide ();
312 _editor->pop_canvas_cursor ();
314 return _move_threshold_passed;
318 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
322 if (f > _pointer_frame_offset) {
323 pos = f - _pointer_frame_offset;
327 _editor->snap_to_with_modifier (pos, event);
334 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
336 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
340 Drag::current_pointer_y () const
342 if (!_trackview_only) {
343 return _drags->current_pointer_y ();
346 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
350 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
352 /* check to see if we have moved in any way that matters since the last motion event */
353 if (_move_threshold_passed &&
354 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
355 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
359 pair<framecnt_t, int> const threshold = move_threshold ();
361 bool const old_move_threshold_passed = _move_threshold_passed;
363 if (!from_autoscroll && !_move_threshold_passed) {
365 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
366 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
368 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
371 if (active (_editor->mouse_mode) && _move_threshold_passed) {
373 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
374 if (!from_autoscroll) {
375 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
378 if (!_editor->autoscroll_active() || from_autoscroll) {
379 motion (event, _move_threshold_passed != old_move_threshold_passed);
381 _last_pointer_x = _drags->current_pointer_x ();
382 _last_pointer_y = current_pointer_y ();
383 _last_pointer_frame = adjusted_current_frame (event);
393 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
401 aborted (_move_threshold_passed);
403 _editor->stop_canvas_autoscroll ();
404 _editor->verbose_cursor()->hide ();
408 Drag::show_verbose_cursor_time (framepos_t frame)
410 /* We use DragManager::current_pointer_y() here
411 because we need to position the verbose canvas
412 cursor within the overall canvas, regardless
413 of this particular drag's _trackview_only
417 _editor->verbose_cursor()->set_time (
419 _drags->current_pointer_x() + 10,
420 _drags->current_pointer_y() + 10
423 _editor->verbose_cursor()->show ();
427 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
429 _editor->verbose_cursor()->show (xoffset);
431 /* We use DragManager::current_pointer_y() here
432 because we need to position the verbose canvas
433 cursor within the overall canvas, regardless
434 of this particular drag's _trackview_only
438 _editor->verbose_cursor()->set_duration (
440 _drags->current_pointer_x() + 10,
441 _drags->current_pointer_y() + 10
446 Drag::show_verbose_cursor_text (string const & text)
448 _editor->verbose_cursor()->show ();
450 /* We use DragManager::current_pointer_y() here
451 because we need to position the verbose canvas
452 cursor within the overall canvas, regardless
453 of this particular drag's _trackview_only
457 _editor->verbose_cursor()->set (
459 _drags->current_pointer_x() + 10,
460 _drags->current_pointer_y() + 10
464 boost::shared_ptr<Region>
465 Drag::add_midi_region (MidiTimeAxisView* view)
467 if (_editor->session()) {
468 const TempoMap& map (_editor->session()->tempo_map());
469 framecnt_t pos = grab_frame();
470 const Meter& m = map.meter_at (pos);
471 /* not that the frame rate used here can be affected by pull up/down which
474 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
475 return view->add_region (grab_frame(), len, true);
478 return boost::shared_ptr<Region>();
481 struct EditorOrderTimeAxisViewSorter {
482 bool operator() (TimeAxisView* a, TimeAxisView* b) {
483 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
484 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
486 return ra->route()->order_key () < rb->route()->order_key ();
490 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
494 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
496 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
497 as some of the regions we are dragging may be on such tracks.
500 TrackViewList track_views = _editor->track_views;
501 track_views.sort (EditorOrderTimeAxisViewSorter ());
503 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
504 _time_axis_views.push_back (*i);
506 TimeAxisView::Children children_list = (*i)->get_child_list ();
507 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
508 _time_axis_views.push_back (j->get());
512 /* the list of views can be empty at this point if this is a region list-insert drag
515 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
516 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
519 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
523 RegionDrag::region_going_away (RegionView* v)
525 list<DraggingView>::iterator i = _views.begin ();
526 while (i != _views.end() && i->view != v) {
530 if (i != _views.end()) {
535 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
536 * or -1 if it is not found.
539 RegionDrag::find_time_axis_view (TimeAxisView* t) const
542 int const N = _time_axis_views.size ();
543 while (i < N && _time_axis_views[i] != t) {
554 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
555 : RegionDrag (e, i, p, v)
558 , _last_pointer_time_axis_view (0)
559 , _last_pointer_layer (0)
561 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
565 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
567 Drag::start_grab (event, cursor);
569 show_verbose_cursor_time (_last_frame_position);
571 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
573 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
574 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
579 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
581 /* compute the amount of pointer motion in frames, and where
582 the region would be if we moved it by that much.
584 *pending_region_position = adjusted_current_frame (event);
586 framepos_t sync_frame;
587 framecnt_t sync_offset;
590 sync_offset = _primary->region()->sync_offset (sync_dir);
592 /* we don't handle a sync point that lies before zero.
594 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
596 sync_frame = *pending_region_position + (sync_dir*sync_offset);
598 _editor->snap_to_with_modifier (sync_frame, event);
600 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
603 *pending_region_position = _last_frame_position;
606 if (*pending_region_position > max_framepos - _primary->region()->length()) {
607 *pending_region_position = _last_frame_position;
612 /* in locked edit mode, reverse the usual meaning of _x_constrained */
613 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
615 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
617 /* x movement since last time (in pixels) */
618 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
620 /* total x movement */
621 framecnt_t total_dx = *pending_region_position;
622 if (regions_came_from_canvas()) {
623 total_dx = total_dx - grab_frame ();
626 /* check that no regions have gone off the start of the session */
627 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
628 if ((i->view->region()->position() + total_dx) < 0) {
630 *pending_region_position = _last_frame_position;
635 _last_frame_position = *pending_region_position;
642 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
644 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
645 int const n = i->time_axis_view + delta_track;
646 if (n < 0 || n >= int (_time_axis_views.size())) {
647 /* off the top or bottom track */
651 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
652 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
653 /* not a track, or the wrong type */
657 double const l = i->layer + delta_layer;
659 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
660 mode to allow the user to place a region below another on layer 0.
662 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
663 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
664 If it has, the layers will be munged later anyway, so it's ok.
670 /* all regions being dragged are ok with this change */
675 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
677 double delta_layer = 0;
678 int delta_time_axis_view = 0;
680 assert (!_views.empty ());
682 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
684 /* Find the TimeAxisView that the pointer is now over */
685 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
686 TimeAxisView* tv = r.first;
688 if (tv && tv->view()) {
689 double layer = r.second;
691 if (first_move && tv->view()->layer_display() == Stacked) {
692 tv->view()->set_layer_display (Expanded);
695 /* Here's the current pointer position in terms of time axis view and layer */
696 int const current_pointer_time_axis_view = find_time_axis_view (tv);
697 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
699 /* Work out the change in y */
701 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
702 delta_layer = current_pointer_layer - _last_pointer_layer;
705 /* Work out the change in x */
706 framepos_t pending_region_position;
707 double const x_delta = compute_x_delta (event, &pending_region_position);
709 /* Verify change in y */
710 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
711 /* this y movement is not allowed, so do no y movement this time */
712 delta_time_axis_view = 0;
716 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
717 /* haven't reached next snap point, and we're not switching
718 trackviews nor layers. nothing to do.
723 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
725 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
727 RegionView* rv = i->view;
729 if (rv->region()->locked() || rv->region()->video_locked()) {
736 /* reparent the regionview into a group above all
740 ArdourCanvas::Item* rvg = rv->get_canvas_group();
741 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
742 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
743 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
744 /* move the item so that it continues to appear at the
745 same location now that its parent has changed.
747 rvg->move (rv_canvas_offset - dmg_canvas_offset);
750 /* If we have moved tracks, we'll fudge the layer delta so that the
751 region gets moved back onto layer 0 on its new track; this avoids
752 confusion when dragging regions from non-zero layers onto different
755 double this_delta_layer = delta_layer;
756 if (delta_time_axis_view != 0) {
757 this_delta_layer = - i->layer;
764 if (i->time_axis_view >= 0) {
765 track_index = i->time_axis_view + delta_time_axis_view;
767 track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
770 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
774 /* The TimeAxisView that this region is now over */
775 TimeAxisView* current_tv = _time_axis_views[track_index];
777 /* Ensure it is moved from stacked -> expanded if appropriate */
778 if (current_tv->view()->layer_display() == Stacked) {
779 current_tv->view()->set_layer_display (Expanded);
782 /* We're only allowed to go -ve in layer on Expanded views */
783 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
784 this_delta_layer = - i->layer;
788 rv->set_height (current_tv->view()->child_height ());
790 /* Update show/hidden status as the region view may have come from a hidden track,
791 or have moved to one.
793 if (current_tv->hidden ()) {
794 rv->get_canvas_group()->hide ();
796 rv->get_canvas_group()->show ();
799 /* Update the DraggingView */
800 i->time_axis_view = track_index;
801 i->layer += this_delta_layer;
804 _editor->mouse_brush_insert_region (rv, pending_region_position);
808 /* Get the y coordinate of the top of the track that this region is now over */
809 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
811 /* And adjust for the layer that it should be on */
812 StreamView* cv = current_tv->view ();
813 switch (cv->layer_display ()) {
817 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
820 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
824 /* need to get the parent of the regionview
825 * canvas group and get its position in
826 * equivalent coordinate space as the trackview
827 * we are now dragging over.
830 /* Now move the region view */
831 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->canvas_origin().y);
835 /* Only move the region into the empty dropzone at the bottom if the pointer
839 if (current_pointer_y() >= 0) {
841 Coord last_track_bottom_edge;
842 if (!_time_axis_views.empty()) {
843 TimeAxisView* last = _time_axis_views.back();
844 last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
846 last_track_bottom_edge = 0;
849 rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y);
850 i->time_axis_view = -1;
854 } /* foreach region */
856 _total_x_delta += x_delta;
858 if (x_delta != 0 && !_brushing) {
859 show_verbose_cursor_time (_last_frame_position);
862 _last_pointer_time_axis_view += delta_time_axis_view;
863 _last_pointer_layer += delta_layer;
867 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
869 if (_copy && first_move) {
871 /* duplicate the regionview(s) and region(s) */
873 list<DraggingView> new_regionviews;
875 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
877 RegionView* rv = i->view;
878 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
879 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
881 const boost::shared_ptr<const Region> original = rv->region();
882 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
883 region_copy->set_position (original->position());
887 boost::shared_ptr<AudioRegion> audioregion_copy
888 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
890 nrv = new AudioRegionView (*arv, audioregion_copy);
892 boost::shared_ptr<MidiRegion> midiregion_copy
893 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
894 nrv = new MidiRegionView (*mrv, midiregion_copy);
899 nrv->get_canvas_group()->show ();
900 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
902 /* swap _primary to the copy */
904 if (rv == _primary) {
908 /* ..and deselect the one we copied */
910 rv->set_selected (false);
913 if (!new_regionviews.empty()) {
915 /* reflect the fact that we are dragging the copies */
917 _views = new_regionviews;
919 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
923 RegionMotionDrag::motion (event, first_move);
927 RegionMotionDrag::finished (GdkEvent *, bool)
929 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
934 if ((*i)->view()->layer_display() == Expanded) {
935 (*i)->view()->set_layer_display (Stacked);
941 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
943 RegionMotionDrag::finished (ev, movement_occurred);
945 if (!movement_occurred) {
949 if (was_double_click() && !_views.empty()) {
950 DraggingView dv = _views.front();
951 dv.view->show_region_editor ();
958 /* reverse this here so that we have the correct logic to finalize
962 if (Config->get_edit_mode() == Lock) {
963 _x_constrained = !_x_constrained;
966 assert (!_views.empty ());
968 /* We might have hidden region views so that they weren't visible during the drag
969 (when they have been reparented). Now everything can be shown again, as region
970 views are back in their track parent groups.
972 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
973 i->view->get_canvas_group()->show ();
976 bool const changed_position = (_last_frame_position != _primary->region()->position());
977 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
978 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
998 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1002 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1004 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1009 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1010 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1011 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1012 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1014 rtav->set_height (original->current_height());
1018 ChanCount one_midi_port (DataType::MIDI, 1);
1019 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1020 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1021 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1023 rtav->set_height (original->current_height());
1028 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1034 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1036 RegionSelection new_views;
1037 PlaylistSet modified_playlists;
1038 RouteTimeAxisView* new_time_axis_view = 0;
1041 /* all changes were made during motion event handlers */
1043 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1047 _editor->commit_reversible_command ();
1051 if (_x_constrained) {
1052 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1054 _editor->begin_reversible_command (Operations::region_copy);
1057 /* insert the regions into their new playlists */
1058 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1060 RouteTimeAxisView* dest_rtv = 0;
1062 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1068 if (changed_position && !_x_constrained) {
1069 where = i->view->region()->position() - drag_delta;
1071 where = i->view->region()->position();
1074 if (i->time_axis_view < 0) {
1075 if (!new_time_axis_view) {
1076 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1078 dest_rtv = new_time_axis_view;
1080 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1083 if (dest_rtv != 0) {
1084 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1085 if (new_view != 0) {
1086 new_views.push_back (new_view);
1090 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1091 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1094 list<DraggingView>::const_iterator next = i;
1100 /* If we've created new regions either by copying or moving
1101 to a new track, we want to replace the old selection with the new ones
1104 if (new_views.size() > 0) {
1105 _editor->selection->set (new_views);
1108 /* write commands for the accumulated diffs for all our modified playlists */
1109 add_stateful_diff_commands_for_playlists (modified_playlists);
1111 _editor->commit_reversible_command ();
1115 RegionMoveDrag::finished_no_copy (
1116 bool const changed_position,
1117 bool const changed_tracks,
1118 framecnt_t const drag_delta
1121 RegionSelection new_views;
1122 PlaylistSet modified_playlists;
1123 PlaylistSet frozen_playlists;
1124 set<RouteTimeAxisView*> views_to_update;
1125 RouteTimeAxisView* new_time_axis_view = 0;
1128 /* all changes were made during motion event handlers */
1129 _editor->commit_reversible_command ();
1133 if (_x_constrained) {
1134 _editor->begin_reversible_command (_("fixed time region drag"));
1136 _editor->begin_reversible_command (Operations::region_drag);
1139 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1141 RegionView* rv = i->view;
1142 RouteTimeAxisView* dest_rtv = 0;
1144 if (rv->region()->locked() || rv->region()->video_locked()) {
1149 if (i->time_axis_view < 0) {
1150 if (!new_time_axis_view) {
1151 new_time_axis_view = create_destination_time_axis (rv->region(), i->initial_time_axis_view);
1153 dest_rtv = new_time_axis_view;
1155 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1160 double const dest_layer = i->layer;
1162 views_to_update.insert (dest_rtv);
1166 if (changed_position && !_x_constrained) {
1167 where = rv->region()->position() - drag_delta;
1169 where = rv->region()->position();
1172 if (changed_tracks) {
1174 /* insert into new playlist */
1176 RegionView* new_view = insert_region_into_playlist (
1177 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1180 if (new_view == 0) {
1185 new_views.push_back (new_view);
1187 /* remove from old playlist */
1189 /* the region that used to be in the old playlist is not
1190 moved to the new one - we use a copy of it. as a result,
1191 any existing editor for the region should no longer be
1194 rv->hide_region_editor();
1197 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1201 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1203 /* this movement may result in a crossfade being modified, or a layering change,
1204 so we need to get undo data from the playlist as well as the region.
1207 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1209 playlist->clear_changes ();
1212 rv->region()->clear_changes ();
1215 motion on the same track. plonk the previously reparented region
1216 back to its original canvas group (its streamview).
1217 No need to do anything for copies as they are fake regions which will be deleted.
1220 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1221 rv->get_canvas_group()->set_y_position (i->initial_y);
1224 /* just change the model */
1225 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1226 playlist->set_layer (rv->region(), dest_layer);
1229 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1231 r = frozen_playlists.insert (playlist);
1234 playlist->freeze ();
1237 rv->region()->set_position (where);
1239 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1242 if (changed_tracks) {
1244 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1245 was selected in all of them, then removing it from a playlist will have removed all
1246 trace of it from _views (i.e. there were N regions selected, we removed 1,
1247 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1248 corresponding regionview, and _views is now empty).
1250 This could have invalidated any and all iterators into _views.
1252 The heuristic we use here is: if the region selection is empty, break out of the loop
1253 here. if the region selection is not empty, then restart the loop because we know that
1254 we must have removed at least the region(view) we've just been working on as well as any
1255 that we processed on previous iterations.
1257 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1258 we can just iterate.
1262 if (_views.empty()) {
1273 /* If we've created new regions either by copying or moving
1274 to a new track, we want to replace the old selection with the new ones
1277 if (new_views.size() > 0) {
1278 _editor->selection->set (new_views);
1281 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1285 /* write commands for the accumulated diffs for all our modified playlists */
1286 add_stateful_diff_commands_for_playlists (modified_playlists);
1288 _editor->commit_reversible_command ();
1290 /* We have futzed with the layering of canvas items on our streamviews.
1291 If any region changed layer, this will have resulted in the stream
1292 views being asked to set up their region views, and all will be well.
1293 If not, we might now have badly-ordered region views. Ask the StreamViews
1294 involved to sort themselves out, just in case.
1297 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1298 (*i)->view()->playlist_layered ((*i)->track ());
1302 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1303 * @param region Region to remove.
1304 * @param playlist playlist To remove from.
1305 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1306 * that clear_changes () is only called once per playlist.
1309 RegionMoveDrag::remove_region_from_playlist (
1310 boost::shared_ptr<Region> region,
1311 boost::shared_ptr<Playlist> playlist,
1312 PlaylistSet& modified_playlists
1315 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1318 playlist->clear_changes ();
1321 playlist->remove_region (region);
1325 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1326 * clearing the playlist's diff history first if necessary.
1327 * @param region Region to insert.
1328 * @param dest_rtv Destination RouteTimeAxisView.
1329 * @param dest_layer Destination layer.
1330 * @param where Destination position.
1331 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1332 * that clear_changes () is only called once per playlist.
1333 * @return New RegionView, or 0 if no insert was performed.
1336 RegionMoveDrag::insert_region_into_playlist (
1337 boost::shared_ptr<Region> region,
1338 RouteTimeAxisView* dest_rtv,
1341 PlaylistSet& modified_playlists
1344 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1345 if (!dest_playlist) {
1349 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1350 _new_region_view = 0;
1351 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1353 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1354 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1356 dest_playlist->clear_changes ();
1359 dest_playlist->add_region (region, where);
1361 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1362 dest_playlist->set_layer (region, dest_layer);
1367 assert (_new_region_view);
1369 return _new_region_view;
1373 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1375 _new_region_view = rv;
1379 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1381 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1382 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1384 _editor->session()->add_command (c);
1393 RegionMoveDrag::aborted (bool movement_occurred)
1397 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1404 RegionMotionDrag::aborted (movement_occurred);
1409 RegionMotionDrag::aborted (bool)
1411 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1413 StreamView* sview = (*i)->view();
1416 if (sview->layer_display() == Expanded) {
1417 sview->set_layer_display (Stacked);
1422 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1423 RegionView* rv = i->view;
1424 TimeAxisView* tv = &(rv->get_time_axis_view ());
1425 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1427 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1428 rv->get_canvas_group()->set_y_position (0);
1430 rv->move (-_total_x_delta, 0);
1431 rv->set_height (rtv->view()->child_height ());
1435 /** @param b true to brush, otherwise false.
1436 * @param c true to make copies of the regions being moved, otherwise false.
1438 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1439 : RegionMotionDrag (e, i, p, v, b),
1442 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1445 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1446 if (rtv && rtv->is_track()) {
1447 speed = rtv->track()->speed ();
1450 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1454 RegionMoveDrag::setup_pointer_frame_offset ()
1456 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1459 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1460 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1462 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1464 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1465 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1467 _primary = v->view()->create_region_view (r, false, false);
1469 _primary->get_canvas_group()->show ();
1470 _primary->set_position (pos, 0);
1471 _views.push_back (DraggingView (_primary, this, v));
1473 _last_frame_position = pos;
1475 _item = _primary->get_canvas_group ();
1479 RegionInsertDrag::finished (GdkEvent *, bool)
1481 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1483 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1484 _primary->get_canvas_group()->set_y_position (0);
1486 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1488 _editor->begin_reversible_command (Operations::insert_region);
1489 playlist->clear_changes ();
1490 playlist->add_region (_primary->region (), _last_frame_position);
1491 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1492 _editor->commit_reversible_command ();
1500 RegionInsertDrag::aborted (bool)
1507 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1508 : RegionMoveDrag (e, i, p, v, false, false)
1510 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1513 struct RegionSelectionByPosition {
1514 bool operator() (RegionView*a, RegionView* b) {
1515 return a->region()->position () < b->region()->position();
1520 RegionSpliceDrag::motion (GdkEvent* event, bool)
1522 /* Which trackview is this ? */
1524 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1525 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1527 /* The region motion is only processed if the pointer is over
1531 if (!tv || !tv->is_track()) {
1532 /* To make sure we hide the verbose canvas cursor when the mouse is
1533 not held over and audiotrack.
1535 _editor->verbose_cursor()->hide ();
1541 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1547 RegionSelection copy (_editor->selection->regions);
1549 RegionSelectionByPosition cmp;
1552 framepos_t const pf = adjusted_current_frame (event);
1554 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1556 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1562 boost::shared_ptr<Playlist> playlist;
1564 if ((playlist = atv->playlist()) == 0) {
1568 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1573 if (pf < (*i)->region()->last_frame() + 1) {
1577 if (pf > (*i)->region()->first_frame()) {
1583 playlist->shuffle ((*i)->region(), dir);
1588 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1590 RegionMoveDrag::finished (event, movement_occurred);
1594 RegionSpliceDrag::aborted (bool)
1599 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1601 _view (dynamic_cast<MidiTimeAxisView*> (v))
1603 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1609 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1612 _region = add_midi_region (_view);
1613 _view->playlist()->freeze ();
1616 framepos_t const f = adjusted_current_frame (event);
1617 if (f < grab_frame()) {
1618 _region->set_position (f);
1621 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1622 so that if this region is duplicated, its duplicate starts on
1623 a snap point rather than 1 frame after a snap point. Otherwise things get
1624 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1625 place snapped notes at the start of the region.
1628 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1629 _region->set_length (len < 1 ? 1 : len);
1635 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1637 if (!movement_occurred) {
1638 add_midi_region (_view);
1640 _view->playlist()->thaw ();
1645 RegionCreateDrag::aborted (bool)
1648 _view->playlist()->thaw ();
1654 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1658 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1662 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1664 Gdk::Cursor* cursor;
1665 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1667 float x_fraction = cnote->mouse_x_fraction ();
1669 if (x_fraction > 0.0 && x_fraction < 0.25) {
1670 cursor = _editor->cursors()->left_side_trim;
1672 cursor = _editor->cursors()->right_side_trim;
1675 Drag::start_grab (event, cursor);
1677 region = &cnote->region_view();
1679 double const region_start = region->get_position_pixels();
1680 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1682 if (grab_x() <= middle_point) {
1683 cursor = _editor->cursors()->left_side_trim;
1686 cursor = _editor->cursors()->right_side_trim;
1692 if (event->motion.state & Keyboard::PrimaryModifier) {
1698 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1700 if (ms.size() > 1) {
1701 /* has to be relative, may make no sense otherwise */
1705 /* select this note; if it is already selected, preserve the existing selection,
1706 otherwise make this note the only one selected.
1708 region->note_selected (cnote, cnote->selected ());
1710 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1711 MidiRegionSelection::iterator next;
1714 (*r)->begin_resizing (at_front);
1720 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1722 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1723 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1724 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1726 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1731 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1733 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1734 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1735 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1737 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1742 NoteResizeDrag::aborted (bool)
1744 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1745 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1746 (*r)->abort_resizing ();
1750 AVDraggingView::AVDraggingView (RegionView* v)
1753 initial_position = v->region()->position ();
1756 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1759 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1762 TrackViewList empty;
1764 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1765 std::list<RegionView*> views = rs.by_layer();
1767 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1768 RegionView* rv = (*i);
1769 if (!rv->region()->video_locked()) {
1772 _views.push_back (AVDraggingView (rv));
1777 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1779 Drag::start_grab (event);
1780 if (_editor->session() == 0) {
1784 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1785 _max_backwards_drag = (
1786 ARDOUR_UI::instance()->video_timeline->get_duration()
1787 + ARDOUR_UI::instance()->video_timeline->get_offset()
1788 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1791 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1792 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1793 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1796 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1799 Timecode::Time timecode;
1800 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1801 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);
1802 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1803 _editor->verbose_cursor()->show ();
1807 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1809 if (_editor->session() == 0) {
1812 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1816 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1817 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
1819 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1820 dt = - _max_backwards_drag;
1823 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1824 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1826 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1827 RegionView* rv = i->view;
1828 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1831 rv->region()->clear_changes ();
1832 rv->region()->suspend_property_changes();
1834 rv->region()->set_position(i->initial_position + dt);
1835 rv->region_changed(ARDOUR::Properties::position);
1838 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1839 Timecode::Time timecode;
1840 Timecode::Time timediff;
1842 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1843 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1844 snprintf (buf, sizeof (buf),
1845 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1846 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1847 , _("Video Start:"),
1848 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1850 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1852 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1853 _editor->verbose_cursor()->show ();
1857 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1859 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1863 if (!movement_occurred || ! _editor->session()) {
1867 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1869 _editor->begin_reversible_command (_("Move Video"));
1871 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1872 ARDOUR_UI::instance()->video_timeline->save_undo();
1873 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1874 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1876 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1877 i->view->drag_end();
1878 i->view->region()->resume_property_changes ();
1880 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1883 _editor->session()->maybe_update_session_range(
1884 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1885 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1889 _editor->commit_reversible_command ();
1893 VideoTimeLineDrag::aborted (bool)
1895 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1898 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1899 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1901 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1902 i->view->region()->resume_property_changes ();
1903 i->view->region()->set_position(i->initial_position);
1907 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1908 : RegionDrag (e, i, p, v)
1909 , _preserve_fade_anchor (preserve_fade_anchor)
1910 , _jump_position_when_done (false)
1912 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1916 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1919 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1920 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1922 if (tv && tv->is_track()) {
1923 speed = tv->track()->speed();
1926 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1927 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1928 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1930 framepos_t const pf = adjusted_current_frame (event);
1932 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1933 /* Move the contents of the region around without changing the region bounds */
1934 _operation = ContentsTrim;
1935 Drag::start_grab (event, _editor->cursors()->trimmer);
1937 /* These will get overridden for a point trim.*/
1938 if (pf < (region_start + region_length/2)) {
1939 /* closer to front */
1940 _operation = StartTrim;
1942 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1943 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
1945 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1949 _operation = EndTrim;
1950 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1951 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
1953 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1958 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1959 _jump_position_when_done = true;
1962 switch (_operation) {
1964 show_verbose_cursor_time (region_start);
1965 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1966 i->view->trim_front_starting ();
1970 show_verbose_cursor_time (region_end);
1973 show_verbose_cursor_time (pf);
1977 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1978 i->view->region()->suspend_property_changes ();
1983 TrimDrag::motion (GdkEvent* event, bool first_move)
1985 RegionView* rv = _primary;
1988 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1989 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1990 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1991 frameoffset_t frame_delta = 0;
1993 if (tv && tv->is_track()) {
1994 speed = tv->track()->speed();
1997 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2003 switch (_operation) {
2005 trim_type = "Region start trim";
2008 trim_type = "Region end trim";
2011 trim_type = "Region content trim";
2018 _editor->begin_reversible_command (trim_type);
2020 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2021 RegionView* rv = i->view;
2022 rv->enable_display (false);
2023 rv->region()->playlist()->clear_owned_changes ();
2025 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2028 arv->temporarily_hide_envelope ();
2032 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2033 insert_result = _editor->motion_frozen_playlists.insert (pl);
2035 if (insert_result.second) {
2041 bool non_overlap_trim = false;
2043 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2044 non_overlap_trim = true;
2047 /* contstrain trim to fade length */
2048 if (_preserve_fade_anchor) {
2049 switch (_operation) {
2051 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2052 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2054 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2055 if (ar->locked()) continue;
2056 framecnt_t len = ar->fade_in()->back()->when;
2057 if (len < dt) dt = min(dt, len);
2061 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2062 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2064 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2065 if (ar->locked()) continue;
2066 framecnt_t len = ar->fade_out()->back()->when;
2067 if (len < -dt) dt = max(dt, -len);
2076 switch (_operation) {
2078 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2079 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2080 if (changed && _preserve_fade_anchor) {
2081 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2083 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2084 framecnt_t len = ar->fade_in()->back()->when;
2085 framecnt_t diff = ar->first_frame() - i->initial_position;
2086 framepos_t new_length = len - diff;
2087 i->anchored_fade_length = min (ar->length(), new_length);
2088 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2089 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2096 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2097 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2098 if (changed && _preserve_fade_anchor) {
2099 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2101 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2102 framecnt_t len = ar->fade_out()->back()->when;
2103 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2104 framepos_t new_length = len + diff;
2105 i->anchored_fade_length = min (ar->length(), new_length);
2106 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2107 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2115 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2117 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2118 i->view->move_contents (frame_delta);
2124 switch (_operation) {
2126 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2129 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2132 // show_verbose_cursor_time (frame_delta);
2139 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2141 if (movement_occurred) {
2142 motion (event, false);
2144 if (_operation == StartTrim) {
2145 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2147 /* This must happen before the region's StatefulDiffCommand is created, as it may
2148 `correct' (ahem) the region's _start from being negative to being zero. It
2149 needs to be zero in the undo record.
2151 i->view->trim_front_ending ();
2153 if (_preserve_fade_anchor) {
2154 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2156 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2157 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2158 ar->set_fade_in_length(i->anchored_fade_length);
2159 ar->set_fade_in_active(true);
2162 if (_jump_position_when_done) {
2163 i->view->region()->set_position (i->initial_position);
2166 } else if (_operation == EndTrim) {
2167 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2168 if (_preserve_fade_anchor) {
2169 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2171 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2172 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2173 ar->set_fade_out_length(i->anchored_fade_length);
2174 ar->set_fade_out_active(true);
2177 if (_jump_position_when_done) {
2178 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2183 if (!_views.empty()) {
2184 if (_operation == StartTrim) {
2185 _editor->maybe_locate_with_edit_preroll(
2186 _views.begin()->view->region()->position());
2188 if (_operation == EndTrim) {
2189 _editor->maybe_locate_with_edit_preroll(
2190 _views.begin()->view->region()->position() +
2191 _views.begin()->view->region()->length());
2195 if (!_editor->selection->selected (_primary)) {
2196 _primary->thaw_after_trim ();
2199 set<boost::shared_ptr<Playlist> > diffed_playlists;
2201 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2202 i->view->thaw_after_trim ();
2203 i->view->enable_display (true);
2205 /* Trimming one region may affect others on the playlist, so we need
2206 to get undo Commands from the whole playlist rather than just the
2207 region. Use diffed_playlists to make sure we don't diff a given
2208 playlist more than once.
2210 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2211 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2212 vector<Command*> cmds;
2214 _editor->session()->add_commands (cmds);
2215 diffed_playlists.insert (p);
2220 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2224 _editor->motion_frozen_playlists.clear ();
2225 _editor->commit_reversible_command();
2228 /* no mouse movement */
2229 _editor->point_trim (event, adjusted_current_frame (event));
2232 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2233 if (_operation == StartTrim) {
2234 i->view->trim_front_ending ();
2237 i->view->region()->resume_property_changes ();
2242 TrimDrag::aborted (bool movement_occurred)
2244 /* Our motion method is changing model state, so use the Undo system
2245 to cancel. Perhaps not ideal, as this will leave an Undo point
2246 behind which may be slightly odd from the user's point of view.
2251 if (movement_occurred) {
2255 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2256 i->view->region()->resume_property_changes ();
2261 TrimDrag::setup_pointer_frame_offset ()
2263 list<DraggingView>::iterator i = _views.begin ();
2264 while (i != _views.end() && i->view != _primary) {
2268 if (i == _views.end()) {
2272 switch (_operation) {
2274 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2277 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2284 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2288 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2289 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2294 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2296 Drag::start_grab (event, cursor);
2297 show_verbose_cursor_time (adjusted_current_frame(event));
2301 MeterMarkerDrag::setup_pointer_frame_offset ()
2303 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2307 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2309 if (!_marker->meter().movable()) {
2315 // create a dummy marker for visual representation of moving the
2316 // section, because whether its a copy or not, we're going to
2317 // leave or lose the original marker (leave if its a copy; lose if its
2318 // not, because we'll remove it from the map).
2320 MeterSection section (_marker->meter());
2322 if (!section.movable()) {
2327 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2329 _marker = new MeterMarker (
2331 *_editor->meter_group,
2332 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2334 *new MeterSection (_marker->meter())
2337 /* use the new marker for the grab */
2338 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2341 TempoMap& map (_editor->session()->tempo_map());
2342 /* get current state */
2343 before_state = &map.get_state();
2344 /* remove the section while we drag it */
2345 map.remove_meter (section, true);
2349 framepos_t const pf = adjusted_current_frame (event);
2350 _marker->set_position (pf);
2351 show_verbose_cursor_time (pf);
2355 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2357 if (!movement_occurred) {
2358 if (was_double_click()) {
2359 _editor->edit_meter_marker (*_marker);
2364 if (!_marker->meter().movable()) {
2368 motion (event, false);
2370 Timecode::BBT_Time when;
2372 TempoMap& map (_editor->session()->tempo_map());
2373 map.bbt_time (last_pointer_frame(), when);
2375 if (_copy == true) {
2376 _editor->begin_reversible_command (_("copy meter mark"));
2377 XMLNode &before = map.get_state();
2378 map.add_meter (_marker->meter(), when);
2379 XMLNode &after = map.get_state();
2380 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2381 _editor->commit_reversible_command ();
2384 _editor->begin_reversible_command (_("move meter mark"));
2386 /* we removed it before, so add it back now */
2388 map.add_meter (_marker->meter(), when);
2389 XMLNode &after = map.get_state();
2390 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2391 _editor->commit_reversible_command ();
2394 // delete the dummy marker we used for visual representation while moving.
2395 // a new visual marker will show up automatically.
2400 MeterMarkerDrag::aborted (bool moved)
2402 _marker->set_position (_marker->meter().frame ());
2405 TempoMap& map (_editor->session()->tempo_map());
2406 /* we removed it before, so add it back now */
2407 map.add_meter (_marker->meter(), _marker->meter().frame());
2408 // delete the dummy marker we used for visual representation while moving.
2409 // a new visual marker will show up automatically.
2414 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2418 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2420 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2425 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2427 Drag::start_grab (event, cursor);
2428 show_verbose_cursor_time (adjusted_current_frame (event));
2432 TempoMarkerDrag::setup_pointer_frame_offset ()
2434 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2438 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2440 if (!_marker->tempo().movable()) {
2446 // create a dummy marker for visual representation of moving the
2447 // section, because whether its a copy or not, we're going to
2448 // leave or lose the original marker (leave if its a copy; lose if its
2449 // not, because we'll remove it from the map).
2451 // create a dummy marker for visual representation of moving the copy.
2452 // The actual copying is not done before we reach the finish callback.
2455 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2457 TempoSection section (_marker->tempo());
2459 _marker = new TempoMarker (
2461 *_editor->tempo_group,
2462 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2464 *new TempoSection (_marker->tempo())
2467 /* use the new marker for the grab */
2468 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2471 TempoMap& map (_editor->session()->tempo_map());
2472 /* get current state */
2473 before_state = &map.get_state();
2474 /* remove the section while we drag it */
2475 map.remove_tempo (section, true);
2479 framepos_t const pf = adjusted_current_frame (event);
2480 _marker->set_position (pf);
2481 show_verbose_cursor_time (pf);
2485 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2487 if (!movement_occurred) {
2488 if (was_double_click()) {
2489 _editor->edit_tempo_marker (*_marker);
2494 if (!_marker->tempo().movable()) {
2498 motion (event, false);
2500 TempoMap& map (_editor->session()->tempo_map());
2501 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2502 Timecode::BBT_Time when;
2504 map.bbt_time (beat_time, when);
2506 if (_copy == true) {
2507 _editor->begin_reversible_command (_("copy tempo mark"));
2508 XMLNode &before = map.get_state();
2509 map.add_tempo (_marker->tempo(), when);
2510 XMLNode &after = map.get_state();
2511 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2512 _editor->commit_reversible_command ();
2515 _editor->begin_reversible_command (_("move tempo mark"));
2516 /* we removed it before, so add it back now */
2517 map.add_tempo (_marker->tempo(), when);
2518 XMLNode &after = map.get_state();
2519 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2520 _editor->commit_reversible_command ();
2523 // delete the dummy marker we used for visual representation while moving.
2524 // a new visual marker will show up automatically.
2529 TempoMarkerDrag::aborted (bool moved)
2531 _marker->set_position (_marker->tempo().frame());
2533 TempoMap& map (_editor->session()->tempo_map());
2534 /* we removed it before, so add it back now */
2535 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2536 // delete the dummy marker we used for visual representation while moving.
2537 // a new visual marker will show up automatically.
2542 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2543 : Drag (e, &c.track_canvas_item(), false)
2547 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2550 /** Do all the things we do when dragging the playhead to make it look as though
2551 * we have located, without actually doing the locate (because that would cause
2552 * the diskstream buffers to be refilled, which is too slow).
2555 CursorDrag::fake_locate (framepos_t t)
2557 _editor->playhead_cursor->set_position (t);
2559 Session* s = _editor->session ();
2560 if (s->timecode_transmission_suspended ()) {
2561 framepos_t const f = _editor->playhead_cursor->current_frame ();
2562 /* This is asynchronous so it will be sent "now"
2564 s->send_mmc_locate (f);
2565 /* These are synchronous and will be sent during the next
2568 s->queue_full_time_code ();
2569 s->queue_song_position_pointer ();
2572 show_verbose_cursor_time (t);
2573 _editor->UpdateAllTransportClocks (t);
2577 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2579 Drag::start_grab (event, c);
2581 _grab_zoom = _editor->samples_per_pixel;
2583 framepos_t where = _editor->canvas_event_sample (event);
2585 _editor->snap_to_with_modifier (where, event);
2587 _editor->_dragging_playhead = true;
2589 Session* s = _editor->session ();
2591 /* grab the track canvas item as well */
2593 _cursor.track_canvas_item().grab();
2596 if (_was_rolling && _stop) {
2600 if (s->is_auditioning()) {
2601 s->cancel_audition ();
2605 if (AudioEngine::instance()->connected()) {
2607 /* do this only if we're the engine is connected
2608 * because otherwise this request will never be
2609 * serviced and we'll busy wait forever. likewise,
2610 * notice if we are disconnected while waiting for the
2611 * request to be serviced.
2614 s->request_suspend_timecode_transmission ();
2615 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2616 /* twiddle our thumbs */
2621 fake_locate (where);
2625 CursorDrag::motion (GdkEvent* event, bool)
2627 framepos_t const adjusted_frame = adjusted_current_frame (event);
2628 if (adjusted_frame != last_pointer_frame()) {
2629 fake_locate (adjusted_frame);
2634 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2636 _editor->_dragging_playhead = false;
2638 _cursor.track_canvas_item().ungrab();
2640 if (!movement_occurred && _stop) {
2644 motion (event, false);
2646 Session* s = _editor->session ();
2648 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2649 _editor->_pending_locate_request = true;
2650 s->request_resume_timecode_transmission ();
2655 CursorDrag::aborted (bool)
2657 _cursor.track_canvas_item().ungrab();
2659 if (_editor->_dragging_playhead) {
2660 _editor->session()->request_resume_timecode_transmission ();
2661 _editor->_dragging_playhead = false;
2664 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2667 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2668 : RegionDrag (e, i, p, v)
2670 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2674 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2676 Drag::start_grab (event, cursor);
2678 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2679 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2681 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2685 FadeInDrag::setup_pointer_frame_offset ()
2687 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2688 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2689 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2693 FadeInDrag::motion (GdkEvent* event, bool)
2695 framecnt_t fade_length;
2697 framepos_t const pos = adjusted_current_frame (event);
2699 boost::shared_ptr<Region> region = _primary->region ();
2701 if (pos < (region->position() + 64)) {
2702 fade_length = 64; // this should be a minimum defined somewhere
2703 } else if (pos > region->last_frame()) {
2704 fade_length = region->length();
2706 fade_length = pos - region->position();
2709 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2711 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2717 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2720 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2724 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2726 if (!movement_occurred) {
2730 framecnt_t fade_length;
2732 framepos_t const pos = adjusted_current_frame (event);
2734 boost::shared_ptr<Region> region = _primary->region ();
2736 if (pos < (region->position() + 64)) {
2737 fade_length = 64; // this should be a minimum defined somewhere
2738 } else if (pos > region->last_frame()) {
2739 fade_length = region->length();
2741 fade_length = pos - region->position();
2744 _editor->begin_reversible_command (_("change fade in length"));
2746 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2748 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2754 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2755 XMLNode &before = alist->get_state();
2757 tmp->audio_region()->set_fade_in_length (fade_length);
2758 tmp->audio_region()->set_fade_in_active (true);
2760 XMLNode &after = alist->get_state();
2761 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2764 _editor->commit_reversible_command ();
2768 FadeInDrag::aborted (bool)
2770 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2771 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2777 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2781 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2782 : RegionDrag (e, i, p, v)
2784 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2788 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2790 Drag::start_grab (event, cursor);
2792 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2793 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2795 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2799 FadeOutDrag::setup_pointer_frame_offset ()
2801 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2802 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2803 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2807 FadeOutDrag::motion (GdkEvent* event, bool)
2809 framecnt_t fade_length;
2811 framepos_t const pos = adjusted_current_frame (event);
2813 boost::shared_ptr<Region> region = _primary->region ();
2815 if (pos > (region->last_frame() - 64)) {
2816 fade_length = 64; // this should really be a minimum fade defined somewhere
2818 else if (pos < region->position()) {
2819 fade_length = region->length();
2822 fade_length = region->last_frame() - pos;
2825 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2827 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2833 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2836 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2840 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2842 if (!movement_occurred) {
2846 framecnt_t fade_length;
2848 framepos_t const pos = adjusted_current_frame (event);
2850 boost::shared_ptr<Region> region = _primary->region ();
2852 if (pos > (region->last_frame() - 64)) {
2853 fade_length = 64; // this should really be a minimum fade defined somewhere
2855 else if (pos < region->position()) {
2856 fade_length = region->length();
2859 fade_length = region->last_frame() - pos;
2862 _editor->begin_reversible_command (_("change fade out length"));
2864 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2866 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2872 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2873 XMLNode &before = alist->get_state();
2875 tmp->audio_region()->set_fade_out_length (fade_length);
2876 tmp->audio_region()->set_fade_out_active (true);
2878 XMLNode &after = alist->get_state();
2879 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2882 _editor->commit_reversible_command ();
2886 FadeOutDrag::aborted (bool)
2888 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2889 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2895 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2899 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2902 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2904 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2907 _points.push_back (ArdourCanvas::Duple (0, 0));
2908 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2911 MarkerDrag::~MarkerDrag ()
2913 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2918 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2920 location = new Location (*l);
2921 markers.push_back (m);
2926 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2928 Drag::start_grab (event, cursor);
2932 Location *location = _editor->find_location_from_marker (_marker, is_start);
2933 _editor->_dragging_edit_point = true;
2935 update_item (location);
2937 // _drag_line->show();
2938 // _line->raise_to_top();
2941 show_verbose_cursor_time (location->start());
2943 show_verbose_cursor_time (location->end());
2946 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2949 case Selection::Toggle:
2950 /* we toggle on the button release */
2952 case Selection::Set:
2953 if (!_editor->selection->selected (_marker)) {
2954 _editor->selection->set (_marker);
2957 case Selection::Extend:
2959 Locations::LocationList ll;
2960 list<Marker*> to_add;
2962 _editor->selection->markers.range (s, e);
2963 s = min (_marker->position(), s);
2964 e = max (_marker->position(), e);
2967 if (e < max_framepos) {
2970 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2971 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2972 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2975 to_add.push_back (lm->start);
2978 to_add.push_back (lm->end);
2982 if (!to_add.empty()) {
2983 _editor->selection->add (to_add);
2987 case Selection::Add:
2988 _editor->selection->add (_marker);
2992 /* Set up copies for us to manipulate during the drag
2995 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2997 Location* l = _editor->find_location_from_marker (*i, is_start);
3004 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3006 /* range: check that the other end of the range isn't
3009 CopiedLocationInfo::iterator x;
3010 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3011 if (*(*x).location == *l) {
3015 if (x == _copied_locations.end()) {
3016 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3018 (*x).markers.push_back (*i);
3019 (*x).move_both = true;
3027 MarkerDrag::setup_pointer_frame_offset ()
3030 Location *location = _editor->find_location_from_marker (_marker, is_start);
3031 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3035 MarkerDrag::motion (GdkEvent* event, bool)
3037 framecnt_t f_delta = 0;
3039 bool move_both = false;
3040 Location *real_location;
3041 Location *copy_location = 0;
3043 framepos_t const newframe = adjusted_current_frame (event);
3044 framepos_t next = newframe;
3046 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3050 CopiedLocationInfo::iterator x;
3052 /* find the marker we're dragging, and compute the delta */
3054 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3056 copy_location = (*x).location;
3058 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3060 /* this marker is represented by this
3061 * CopiedLocationMarkerInfo
3064 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3069 if (real_location->is_mark()) {
3070 f_delta = newframe - copy_location->start();
3074 switch (_marker->type()) {
3075 case Marker::SessionStart:
3076 case Marker::RangeStart:
3077 case Marker::LoopStart:
3078 case Marker::PunchIn:
3079 f_delta = newframe - copy_location->start();
3082 case Marker::SessionEnd:
3083 case Marker::RangeEnd:
3084 case Marker::LoopEnd:
3085 case Marker::PunchOut:
3086 f_delta = newframe - copy_location->end();
3089 /* what kind of marker is this ? */
3098 if (x == _copied_locations.end()) {
3099 /* hmm, impossible - we didn't find the dragged marker */
3103 /* now move them all */
3105 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3107 copy_location = x->location;
3109 /* call this to find out if its the start or end */
3111 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3115 if (real_location->locked()) {
3119 if (copy_location->is_mark()) {
3123 copy_location->set_start (copy_location->start() + f_delta);
3127 framepos_t new_start = copy_location->start() + f_delta;
3128 framepos_t new_end = copy_location->end() + f_delta;
3130 if (is_start) { // start-of-range marker
3132 if (move_both || (*x).move_both) {
3133 copy_location->set_start (new_start);
3134 copy_location->set_end (new_end);
3135 } else if (new_start < copy_location->end()) {
3136 copy_location->set_start (new_start);
3137 } else if (newframe > 0) {
3138 _editor->snap_to (next, 1, true);
3139 copy_location->set_end (next);
3140 copy_location->set_start (newframe);
3143 } else { // end marker
3145 if (move_both || (*x).move_both) {
3146 copy_location->set_end (new_end);
3147 copy_location->set_start (new_start);
3148 } else if (new_end > copy_location->start()) {
3149 copy_location->set_end (new_end);
3150 } else if (newframe > 0) {
3151 _editor->snap_to (next, -1, true);
3152 copy_location->set_start (next);
3153 copy_location->set_end (newframe);
3158 update_item (copy_location);
3160 /* now lookup the actual GUI items used to display this
3161 * location and move them to wherever the copy of the location
3162 * is now. This means that the logic in ARDOUR::Location is
3163 * still enforced, even though we are not (yet) modifying
3164 * the real Location itself.
3167 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3170 lm->set_position (copy_location->start(), copy_location->end());
3175 assert (!_copied_locations.empty());
3177 show_verbose_cursor_time (newframe);
3181 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3183 if (!movement_occurred) {
3185 if (was_double_click()) {
3186 _editor->rename_marker (_marker);
3190 /* just a click, do nothing but finish
3191 off the selection process
3194 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3197 case Selection::Set:
3198 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3199 _editor->selection->set (_marker);
3203 case Selection::Toggle:
3204 /* we toggle on the button release, click only */
3205 _editor->selection->toggle (_marker);
3208 case Selection::Extend:
3209 case Selection::Add:
3216 _editor->_dragging_edit_point = false;
3218 _editor->begin_reversible_command ( _("move marker") );
3219 XMLNode &before = _editor->session()->locations()->get_state();
3221 MarkerSelection::iterator i;
3222 CopiedLocationInfo::iterator x;
3225 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3226 x != _copied_locations.end() && i != _editor->selection->markers.end();
3229 Location * location = _editor->find_location_from_marker (*i, is_start);
3233 if (location->locked()) {
3237 if (location->is_mark()) {
3238 location->set_start (((*x).location)->start());
3240 location->set (((*x).location)->start(), ((*x).location)->end());
3245 XMLNode &after = _editor->session()->locations()->get_state();
3246 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3247 _editor->commit_reversible_command ();
3251 MarkerDrag::aborted (bool)
3257 MarkerDrag::update_item (Location*)
3262 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3264 _cumulative_x_drag (0),
3265 _cumulative_y_drag (0)
3267 if (_zero_gain_fraction < 0.0) {
3268 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3271 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3273 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3279 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3281 Drag::start_grab (event, _editor->cursors()->fader);
3283 // start the grab at the center of the control point so
3284 // the point doesn't 'jump' to the mouse after the first drag
3285 _fixed_grab_x = _point->get_x();
3286 _fixed_grab_y = _point->get_y();
3288 float const fraction = 1 - (_point->get_y() / _point->line().height());
3290 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3292 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3293 event->button.x + 10, event->button.y + 10);
3295 _editor->verbose_cursor()->show ();
3297 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3299 if (!_point->can_slide ()) {
3300 _x_constrained = true;
3305 ControlPointDrag::motion (GdkEvent* event, bool)
3307 double dx = _drags->current_pointer_x() - last_pointer_x();
3308 double dy = current_pointer_y() - last_pointer_y();
3310 if (event->button.state & Keyboard::SecondaryModifier) {
3315 /* coordinate in pixels relative to the start of the region (for region-based automation)
3316 or track (for track-based automation) */
3317 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3318 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3320 // calculate zero crossing point. back off by .01 to stay on the
3321 // positive side of zero
3322 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3324 // make sure we hit zero when passing through
3325 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3329 if (_x_constrained) {
3332 if (_y_constrained) {
3336 _cumulative_x_drag = cx - _fixed_grab_x;
3337 _cumulative_y_drag = cy - _fixed_grab_y;
3341 cy = min ((double) _point->line().height(), cy);
3343 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3345 if (!_x_constrained) {
3346 _editor->snap_to_with_modifier (cx_frames, event);
3349 cx_frames = min (cx_frames, _point->line().maximum_time());
3351 float const fraction = 1.0 - (cy / _point->line().height());
3353 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3355 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3359 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3361 if (!movement_occurred) {
3365 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3366 _editor->reset_point_selection ();
3370 motion (event, false);
3373 _point->line().end_drag (_pushing, _final_index);
3374 _editor->session()->commit_reversible_command ();
3378 ControlPointDrag::aborted (bool)
3380 _point->line().reset ();
3384 ControlPointDrag::active (Editing::MouseMode m)
3386 if (m == Editing::MouseGain) {
3387 /* always active in mouse gain */
3391 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3392 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3395 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3398 _cumulative_y_drag (0)
3400 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3404 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3406 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3409 _item = &_line->grab_item ();
3411 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3412 origin, and ditto for y.
3415 double cx = event->button.x;
3416 double cy = event->button.y;
3418 _line->parent_group().canvas_to_item (cx, cy);
3420 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3425 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3426 /* no adjacent points */
3430 Drag::start_grab (event, _editor->cursors()->fader);
3432 /* store grab start in parent frame */
3437 double fraction = 1.0 - (cy / _line->height());
3439 _line->start_drag_line (before, after, fraction);
3441 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3442 event->button.x + 10, event->button.y + 10);
3444 _editor->verbose_cursor()->show ();
3448 LineDrag::motion (GdkEvent* event, bool)
3450 double dy = current_pointer_y() - last_pointer_y();
3452 if (event->button.state & Keyboard::SecondaryModifier) {
3456 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3458 _cumulative_y_drag = cy - _fixed_grab_y;
3461 cy = min ((double) _line->height(), cy);
3463 double const fraction = 1.0 - (cy / _line->height());
3466 /* we are ignoring x position for this drag, so we can just pass in anything */
3467 _line->drag_motion (0, fraction, true, false, ignored);
3469 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3473 LineDrag::finished (GdkEvent* event, bool movement_occured)
3475 if (movement_occured) {
3476 motion (event, false);
3477 _line->end_drag (false, 0);
3479 /* add a new control point on the line */
3481 AutomationTimeAxisView* atv;
3483 _line->end_drag (false, 0);
3485 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3486 framepos_t where = _editor->window_event_sample (event, 0, 0);
3487 atv->add_automation_event (event, where, event->button.y, false);
3491 _editor->session()->commit_reversible_command ();
3495 LineDrag::aborted (bool)
3500 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3503 _cumulative_x_drag (0)
3505 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3509 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3511 Drag::start_grab (event);
3513 _line = reinterpret_cast<Line*> (_item);
3516 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3518 double cx = event->button.x;
3519 double cy = event->button.y;
3521 _item->parent()->canvas_to_item (cx, cy);
3523 /* store grab start in parent frame */
3524 _region_view_grab_x = cx;
3526 _before = *(float*) _item->get_data ("position");
3528 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3530 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3534 FeatureLineDrag::motion (GdkEvent*, bool)
3536 double dx = _drags->current_pointer_x() - last_pointer_x();
3538 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3540 _cumulative_x_drag += dx;
3542 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3551 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3553 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3555 float *pos = new float;
3558 _line->set_data ("position", pos);
3564 FeatureLineDrag::finished (GdkEvent*, bool)
3566 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3567 _arv->update_transient(_before, _before);
3571 FeatureLineDrag::aborted (bool)
3576 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3578 , _vertical_only (false)
3580 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3584 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3586 Drag::start_grab (event);
3587 show_verbose_cursor_time (adjusted_current_frame (event));
3591 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3598 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3600 framepos_t grab = grab_frame ();
3601 if (Config->get_rubberbanding_snaps_to_grid ()) {
3602 _editor->snap_to_with_modifier (grab, event);
3605 /* base start and end on initial click position */
3615 if (current_pointer_y() < grab_y()) {
3616 y1 = current_pointer_y();
3619 y2 = current_pointer_y();
3623 if (start != end || y1 != y2) {
3625 double x1 = _editor->sample_to_pixel (start);
3626 double x2 = _editor->sample_to_pixel (end);
3627 const double min_dimension = 2.0;
3629 if (_vertical_only) {
3630 /* fixed 10 pixel width */
3634 x2 = min (x1 - min_dimension, x2);
3636 x2 = max (x1 + min_dimension, x2);
3641 y2 = min (y1 - min_dimension, y2);
3643 y2 = max (y1 + min_dimension, y2);
3646 /* translate rect into item space and set */
3648 ArdourCanvas::Rect r (x1, y1, x2, y2);
3650 /* this drag is a _trackview_only == true drag, so the y1 and
3651 * y2 (computed using current_pointer_y() and grab_y()) will be
3652 * relative to the top of the trackview group). The
3653 * rubberband rect has the same parent/scroll offset as the
3654 * the trackview group, so we can use the "r" rect directly
3655 * to set the shape of the rubberband.
3658 _editor->rubberband_rect->set (r);
3659 _editor->rubberband_rect->show();
3660 _editor->rubberband_rect->raise_to_top();
3662 show_verbose_cursor_time (pf);
3664 do_select_things (event, true);
3669 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3674 if (grab_frame() < last_pointer_frame()) {
3676 x2 = last_pointer_frame ();
3679 x1 = last_pointer_frame ();
3685 if (current_pointer_y() < grab_y()) {
3686 y1 = current_pointer_y();
3689 y2 = current_pointer_y();
3693 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3697 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3699 if (movement_occurred) {
3701 motion (event, false);
3702 do_select_things (event, false);
3708 bool do_deselect = true;
3709 MidiTimeAxisView* mtv;
3711 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3713 if (_editor->selection->empty()) {
3714 /* nothing selected */
3715 add_midi_region (mtv);
3716 do_deselect = false;
3720 /* do not deselect if Primary or Tertiary (toggle-select or
3721 * extend-select are pressed.
3724 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3725 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3732 _editor->rubberband_rect->hide();
3736 RubberbandSelectDrag::aborted (bool)
3738 _editor->rubberband_rect->hide ();
3741 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3742 : RegionDrag (e, i, p, v)
3744 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3748 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3750 Drag::start_grab (event, cursor);
3752 show_verbose_cursor_time (adjusted_current_frame (event));
3756 TimeFXDrag::motion (GdkEvent* event, bool)
3758 RegionView* rv = _primary;
3759 StreamView* cv = rv->get_time_axis_view().view ();
3761 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3762 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3763 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3765 framepos_t const pf = adjusted_current_frame (event);
3767 if (pf > rv->region()->position()) {
3768 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3771 show_verbose_cursor_time (pf);
3775 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3777 _primary->get_time_axis_view().hide_timestretch ();
3779 if (!movement_occurred) {
3783 if (last_pointer_frame() < _primary->region()->position()) {
3784 /* backwards drag of the left edge - not usable */
3788 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3790 float percentage = (double) newlen / (double) _primary->region()->length();
3792 #ifndef USE_RUBBERBAND
3793 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3794 if (_primary->region()->data_type() == DataType::AUDIO) {
3795 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3799 if (!_editor->get_selection().regions.empty()) {
3800 /* primary will already be included in the selection, and edit
3801 group shared editing will propagate selection across
3802 equivalent regions, so just use the current region
3806 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3807 error << _("An error occurred while executing time stretch operation") << endmsg;
3813 TimeFXDrag::aborted (bool)
3815 _primary->get_time_axis_view().hide_timestretch ();
3818 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3821 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3825 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3827 Drag::start_grab (event);
3831 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3833 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3837 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3839 if (movement_occurred && _editor->session()) {
3840 /* make sure we stop */
3841 _editor->session()->request_transport_speed (0.0);
3846 ScrubDrag::aborted (bool)
3851 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3856 , _original_pointer_time_axis (-1)
3857 , _last_pointer_time_axis (-1)
3858 , _time_selection_at_start (!_editor->get_selection().time.empty())
3860 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3862 if (_time_selection_at_start) {
3863 start_at_start = _editor->get_selection().time.start();
3864 end_at_start = _editor->get_selection().time.end_frame();
3869 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3871 if (_editor->session() == 0) {
3875 Gdk::Cursor* cursor = 0;
3877 switch (_operation) {
3878 case CreateSelection:
3879 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3884 cursor = _editor->cursors()->selector;
3885 Drag::start_grab (event, cursor);
3888 case SelectionStartTrim:
3889 if (_editor->clicked_axisview) {
3890 _editor->clicked_axisview->order_selection_trims (_item, true);
3892 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3895 case SelectionEndTrim:
3896 if (_editor->clicked_axisview) {
3897 _editor->clicked_axisview->order_selection_trims (_item, false);
3899 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3903 Drag::start_grab (event, cursor);
3906 case SelectionExtend:
3907 Drag::start_grab (event, cursor);
3911 if (_operation == SelectionMove) {
3912 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3914 show_verbose_cursor_time (adjusted_current_frame (event));
3917 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
3921 SelectionDrag::setup_pointer_frame_offset ()
3923 switch (_operation) {
3924 case CreateSelection:
3925 _pointer_frame_offset = 0;
3928 case SelectionStartTrim:
3930 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3933 case SelectionEndTrim:
3934 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3937 case SelectionExtend:
3943 SelectionDrag::motion (GdkEvent* event, bool first_move)
3945 framepos_t start = 0;
3947 framecnt_t length = 0;
3948 framecnt_t distance = 0;
3950 framepos_t const pending_position = adjusted_current_frame (event);
3952 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
3956 switch (_operation) {
3957 case CreateSelection:
3959 framepos_t grab = grab_frame ();
3962 grab = adjusted_current_frame (event, false);
3963 if (grab < pending_position) {
3964 _editor->snap_to (grab, -1);
3966 _editor->snap_to (grab, 1);
3970 if (pending_position < grab) {
3971 start = pending_position;
3974 end = pending_position;
3978 /* first drag: Either add to the selection
3979 or create a new selection
3986 /* adding to the selection */
3987 _editor->set_selected_track_as_side_effect (Selection::Add);
3988 _editor->clicked_selection = _editor->selection->add (start, end);
3995 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3996 _editor->set_selected_track_as_side_effect (Selection::Set);
3999 _editor->clicked_selection = _editor->selection->set (start, end);
4003 /* select all tracks within the rectangle that we've marked out so far */
4004 TrackViewList to_be_added_to_selection;
4005 TrackViewList to_be_removed_from_selection;
4006 TrackViewList& all_tracks (_editor->track_views);
4008 ArdourCanvas::Coord const top = grab_y();
4009 ArdourCanvas::Coord const bottom = current_pointer_y();
4011 if (top >= 0 && bottom >= 0) {
4013 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4015 if ((*i)->covered_by_y_range (top, bottom)) {
4016 if (!(*i)->get_selected()) {
4017 to_be_added_to_selection.push_back (*i);
4020 if ((*i)->get_selected()) {
4021 to_be_removed_from_selection.push_back (*i);
4026 if (!to_be_added_to_selection.empty()) {
4027 _editor->selection->add (to_be_added_to_selection);
4030 if (!to_be_removed_from_selection.empty()) {
4031 _editor->selection->remove (to_be_removed_from_selection);
4037 case SelectionStartTrim:
4039 start = _editor->selection->time[_editor->clicked_selection].start;
4040 end = _editor->selection->time[_editor->clicked_selection].end;
4042 if (pending_position > end) {
4045 start = pending_position;
4049 case SelectionEndTrim:
4051 start = _editor->selection->time[_editor->clicked_selection].start;
4052 end = _editor->selection->time[_editor->clicked_selection].end;
4054 if (pending_position < start) {
4057 end = pending_position;
4064 start = _editor->selection->time[_editor->clicked_selection].start;
4065 end = _editor->selection->time[_editor->clicked_selection].end;
4067 length = end - start;
4068 distance = pending_position - start;
4069 start = pending_position;
4070 _editor->snap_to (start);
4072 end = start + length;
4076 case SelectionExtend:
4081 switch (_operation) {
4083 if (_time_selection_at_start) {
4084 _editor->selection->move_time (distance);
4088 _editor->selection->replace (_editor->clicked_selection, start, end);
4092 if (_operation == SelectionMove) {
4093 show_verbose_cursor_time(start);
4095 show_verbose_cursor_time(pending_position);
4100 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4102 Session* s = _editor->session();
4104 if (movement_occurred) {
4105 motion (event, false);
4106 /* XXX this is not object-oriented programming at all. ick */
4107 if (_editor->selection->time.consolidate()) {
4108 _editor->selection->TimeChanged ();
4111 /* XXX what if its a music time selection? */
4113 if ( s->get_play_range() && s->transport_rolling() ) {
4114 s->request_play_range (&_editor->selection->time, true);
4116 if (Config->get_always_play_range() && !s->transport_rolling()) {
4117 s->request_locate (_editor->get_selection().time.start());
4123 /* just a click, no pointer movement.
4126 if (_operation == SelectionExtend) {
4127 if (_time_selection_at_start) {
4128 framepos_t pos = adjusted_current_frame (event, false);
4129 framepos_t start = min (pos, start_at_start);
4130 framepos_t end = max (pos, end_at_start);
4131 _editor->selection->set (start, end);
4134 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4135 if (_editor->clicked_selection) {
4136 _editor->selection->remove (_editor->clicked_selection);
4139 if (!_editor->clicked_selection) {
4140 _editor->selection->clear_time();
4145 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4146 _editor->selection->set (_editor->clicked_axisview);
4149 if (s && s->get_play_range () && s->transport_rolling()) {
4150 s->request_stop (false, false);
4155 _editor->stop_canvas_autoscroll ();
4156 _editor->clicked_selection = 0;
4160 SelectionDrag::aborted (bool)
4165 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4166 : Drag (e, i, false),
4170 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4172 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4173 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4174 physical_screen_height (_editor->get_window())));
4175 _drag_rect->hide ();
4177 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4178 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4182 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4184 if (_editor->session() == 0) {
4188 Gdk::Cursor* cursor = 0;
4190 if (!_editor->temp_location) {
4191 _editor->temp_location = new Location (*_editor->session());
4194 switch (_operation) {
4195 case CreateRangeMarker:
4196 case CreateTransportMarker:
4197 case CreateCDMarker:
4199 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4204 cursor = _editor->cursors()->selector;
4208 Drag::start_grab (event, cursor);
4210 show_verbose_cursor_time (adjusted_current_frame (event));
4214 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4216 framepos_t start = 0;
4218 ArdourCanvas::Rectangle *crect;
4220 switch (_operation) {
4221 case CreateRangeMarker:
4222 crect = _editor->range_bar_drag_rect;
4224 case CreateTransportMarker:
4225 crect = _editor->transport_bar_drag_rect;
4227 case CreateCDMarker:
4228 crect = _editor->cd_marker_bar_drag_rect;
4231 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4236 framepos_t const pf = adjusted_current_frame (event);
4238 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4239 framepos_t grab = grab_frame ();
4240 _editor->snap_to (grab);
4242 if (pf < grab_frame()) {
4250 /* first drag: Either add to the selection
4251 or create a new selection.
4256 _editor->temp_location->set (start, end);
4260 update_item (_editor->temp_location);
4262 //_drag_rect->raise_to_top();
4268 _editor->temp_location->set (start, end);
4270 double x1 = _editor->sample_to_pixel (start);
4271 double x2 = _editor->sample_to_pixel (end);
4275 update_item (_editor->temp_location);
4278 show_verbose_cursor_time (pf);
4283 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4285 Location * newloc = 0;
4289 if (movement_occurred) {
4290 motion (event, false);
4293 switch (_operation) {
4294 case CreateRangeMarker:
4295 case CreateCDMarker:
4297 _editor->begin_reversible_command (_("new range marker"));
4298 XMLNode &before = _editor->session()->locations()->get_state();
4299 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4300 if (_operation == CreateCDMarker) {
4301 flags = Location::IsRangeMarker | Location::IsCDMarker;
4302 _editor->cd_marker_bar_drag_rect->hide();
4305 flags = Location::IsRangeMarker;
4306 _editor->range_bar_drag_rect->hide();
4308 newloc = new Location (
4309 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4312 _editor->session()->locations()->add (newloc, true);
4313 XMLNode &after = _editor->session()->locations()->get_state();
4314 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4315 _editor->commit_reversible_command ();
4319 case CreateTransportMarker:
4320 // popup menu to pick loop or punch
4321 _editor->new_transport_marker_context_menu (&event->button, _item);
4327 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4329 if (_operation == CreateTransportMarker) {
4331 /* didn't drag, so just locate */
4333 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4335 } else if (_operation == CreateCDMarker) {
4337 /* didn't drag, but mark is already created so do
4340 } else { /* operation == CreateRangeMarker */
4346 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4348 if (end == max_framepos) {
4349 end = _editor->session()->current_end_frame ();
4352 if (start == max_framepos) {
4353 start = _editor->session()->current_start_frame ();
4356 switch (_editor->mouse_mode) {
4358 /* find the two markers on either side and then make the selection from it */
4359 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4363 /* find the two markers on either side of the click and make the range out of it */
4364 _editor->selection->set (start, end);
4373 _editor->stop_canvas_autoscroll ();
4377 RangeMarkerBarDrag::aborted (bool)
4383 RangeMarkerBarDrag::update_item (Location* location)
4385 double const x1 = _editor->sample_to_pixel (location->start());
4386 double const x2 = _editor->sample_to_pixel (location->end());
4388 _drag_rect->set_x0 (x1);
4389 _drag_rect->set_x1 (x2);
4392 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4396 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4400 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4402 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4403 Drag::start_grab (event, _editor->cursors()->zoom_out);
4406 Drag::start_grab (event, _editor->cursors()->zoom_in);
4410 show_verbose_cursor_time (adjusted_current_frame (event));
4414 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4419 framepos_t const pf = adjusted_current_frame (event);
4421 framepos_t grab = grab_frame ();
4422 _editor->snap_to_with_modifier (grab, event);
4424 /* base start and end on initial click position */
4436 _editor->zoom_rect->show();
4437 _editor->zoom_rect->raise_to_top();
4440 _editor->reposition_zoom_rect(start, end);
4442 show_verbose_cursor_time (pf);
4447 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4449 if (movement_occurred) {
4450 motion (event, false);
4452 if (grab_frame() < last_pointer_frame()) {
4453 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4455 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4458 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4459 _editor->tav_zoom_step (_zoom_out);
4461 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4465 _editor->zoom_rect->hide();
4469 MouseZoomDrag::aborted (bool)
4471 _editor->zoom_rect->hide ();
4474 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4476 , _cumulative_dx (0)
4477 , _cumulative_dy (0)
4479 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4481 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4483 _region = &_primary->region_view ();
4484 _note_height = _region->midi_stream_view()->note_height ();
4488 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4490 Drag::start_grab (event);
4492 if (!(_was_selected = _primary->selected())) {
4494 /* tertiary-click means extend selection - we'll do that on button release,
4495 so don't add it here, because otherwise we make it hard to figure
4496 out the "extend-to" range.
4499 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4502 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4505 _region->note_selected (_primary, true);
4507 _region->unique_select (_primary);
4513 /** @return Current total drag x change in frames */
4515 NoteDrag::total_dx () const
4518 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4520 /* primary note time */
4521 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4523 /* new time of the primary note in session frames */
4524 frameoffset_t st = n + dx;
4526 framepos_t const rp = _region->region()->position ();
4528 /* prevent the note being dragged earlier than the region's position */
4531 /* snap and return corresponding delta */
4532 return _region->snap_frame_to_frame (st - rp) + rp - n;
4535 /** @return Current total drag y change in note number */
4537 NoteDrag::total_dy () const
4539 MidiStreamView* msv = _region->midi_stream_view ();
4540 double const y = _region->midi_view()->y_position ();
4541 /* new current note */
4542 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4544 n = max (msv->lowest_note(), n);
4545 n = min (msv->highest_note(), n);
4546 /* and work out delta */
4547 return n - msv->y_to_note (grab_y() - y);
4551 NoteDrag::motion (GdkEvent *, bool)
4553 /* Total change in x and y since the start of the drag */
4554 frameoffset_t const dx = total_dx ();
4555 int8_t const dy = total_dy ();
4557 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4558 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4559 double const tdy = -dy * _note_height - _cumulative_dy;
4562 _cumulative_dx += tdx;
4563 _cumulative_dy += tdy;
4565 int8_t note_delta = total_dy();
4567 _region->move_selection (tdx, tdy, note_delta);
4569 /* the new note value may be the same as the old one, but we
4570 * don't know what that means because the selection may have
4571 * involved more than one note and we might be doing something
4572 * odd with them. so show the note value anyway, always.
4576 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4578 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4579 (int) floor ((double)new_note));
4581 show_verbose_cursor_text (buf);
4586 NoteDrag::finished (GdkEvent* ev, bool moved)
4589 /* no motion - select note */
4591 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4592 _editor->current_mouse_mode() == Editing::MouseDraw) {
4594 if (_was_selected) {
4595 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4597 _region->note_deselected (_primary);
4600 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4601 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4603 if (!extend && !add && _region->selection_size() > 1) {
4604 _region->unique_select (_primary);
4605 } else if (extend) {
4606 _region->note_selected (_primary, true, true);
4608 /* it was added during button press */
4613 _region->note_dropped (_primary, total_dx(), total_dy());
4618 NoteDrag::aborted (bool)
4623 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4624 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4625 : Drag (editor, atv->base_item ())
4627 , _nothing_to_drag (false)
4629 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4630 y_origin = atv->y_position();
4631 setup (atv->lines ());
4634 /** Make an AutomationRangeDrag for region gain lines */
4635 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4636 : Drag (editor, rv->get_canvas_group ())
4638 , _nothing_to_drag (false)
4640 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4642 list<boost::shared_ptr<AutomationLine> > lines;
4643 lines.push_back (rv->get_gain_line ());
4644 y_origin = rv->get_time_axis_view().y_position();
4648 /** @param lines AutomationLines to drag.
4649 * @param offset Offset from the session start to the points in the AutomationLines.
4652 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4654 /* find the lines that overlap the ranges being dragged */
4655 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4656 while (i != lines.end ()) {
4657 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4660 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4662 /* check this range against all the AudioRanges that we are using */
4663 list<AudioRange>::const_iterator k = _ranges.begin ();
4664 while (k != _ranges.end()) {
4665 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4671 /* add it to our list if it overlaps at all */
4672 if (k != _ranges.end()) {
4677 _lines.push_back (n);
4683 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4687 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4689 return 1.0 - ((global_y - y_origin) / line->height());
4693 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4695 Drag::start_grab (event, cursor);
4697 /* Get line states before we start changing things */
4698 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4699 i->state = &i->line->get_state ();
4700 i->original_fraction = y_fraction (i->line, current_pointer_y());
4703 if (_ranges.empty()) {
4705 /* No selected time ranges: drag all points */
4706 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4707 uint32_t const N = i->line->npoints ();
4708 for (uint32_t j = 0; j < N; ++j) {
4709 i->points.push_back (i->line->nth (j));
4715 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4717 framecnt_t const half = (i->start + i->end) / 2;
4719 /* find the line that this audio range starts in */
4720 list<Line>::iterator j = _lines.begin();
4721 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4725 if (j != _lines.end()) {
4726 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4728 /* j is the line that this audio range starts in; fade into it;
4729 64 samples length plucked out of thin air.
4732 framepos_t a = i->start + 64;
4737 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4738 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4740 the_list->add (p, the_list->eval (p));
4741 the_list->add (q, the_list->eval (q));
4744 /* same thing for the end */
4747 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4751 if (j != _lines.end()) {
4752 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4754 /* j is the line that this audio range starts in; fade out of it;
4755 64 samples length plucked out of thin air.
4758 framepos_t b = i->end - 64;
4763 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4764 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4766 the_list->add (p, the_list->eval (p));
4767 the_list->add (q, the_list->eval (q));
4771 _nothing_to_drag = true;
4773 /* Find all the points that should be dragged and put them in the relevant
4774 points lists in the Line structs.
4777 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4779 uint32_t const N = i->line->npoints ();
4780 for (uint32_t j = 0; j < N; ++j) {
4782 /* here's a control point on this line */
4783 ControlPoint* p = i->line->nth (j);
4784 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4786 /* see if it's inside a range */
4787 list<AudioRange>::const_iterator k = _ranges.begin ();
4788 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4792 if (k != _ranges.end()) {
4793 /* dragging this point */
4794 _nothing_to_drag = false;
4795 i->points.push_back (p);
4801 if (_nothing_to_drag) {
4805 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4806 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
4811 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4813 if (_nothing_to_drag) {
4817 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4818 float const f = y_fraction (l->line, current_pointer_y());
4819 /* we are ignoring x position for this drag, so we can just pass in anything */
4821 l->line->drag_motion (0, f, true, false, ignored);
4822 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4827 AutomationRangeDrag::finished (GdkEvent* event, bool)
4829 if (_nothing_to_drag) {
4833 motion (event, false);
4834 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4835 i->line->end_drag (false, 0);
4838 _editor->session()->commit_reversible_command ();
4842 AutomationRangeDrag::aborted (bool)
4844 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4849 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
4851 , initial_time_axis_view (itav)
4853 /* note that time_axis_view may be null if the regionview was created
4854 * as part of a copy operation.
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 ();