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"
53 #include "region_gain_line.h"
54 #include "editor_drag.h"
55 #include "audio_time_axis.h"
56 #include "midi_time_axis.h"
57 #include "selection.h"
58 #include "midi_selection.h"
59 #include "automation_time_axis.h"
61 #include "editor_cursors.h"
62 #include "mouse_cursors.h"
63 #include "note_base.h"
64 #include "patch_change.h"
65 #include "verbose_cursor.h"
68 using namespace ARDOUR;
71 using namespace Gtkmm2ext;
72 using namespace Editing;
73 using namespace ArdourCanvas;
75 using Gtkmm2ext::Keyboard;
77 double ControlPointDrag::_zero_gain_fraction = -1.0;
79 DragManager::DragManager (Editor* e)
82 , _current_pointer_frame (0)
86 DragManager::~DragManager ()
91 /** Call abort for each active drag */
97 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
102 if (!_drags.empty ()) {
103 _editor->set_follow_playhead (_old_follow_playhead, false);
112 DragManager::add (Drag* d)
114 d->set_manager (this);
115 _drags.push_back (d);
119 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
121 d->set_manager (this);
122 _drags.push_back (d);
127 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
129 /* Prevent follow playhead during the drag to be nice to the user */
130 _old_follow_playhead = _editor->follow_playhead ();
131 _editor->set_follow_playhead (false);
133 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
135 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
136 (*i)->start_grab (e, c);
140 /** Call end_grab for each active drag.
141 * @return true if any drag reported movement having occurred.
144 DragManager::end_grab (GdkEvent* e)
149 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
150 bool const t = (*i)->end_grab (e);
161 _editor->set_follow_playhead (_old_follow_playhead, false);
167 DragManager::mark_double_click ()
169 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
170 (*i)->set_double_click (true);
175 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
179 /* calling this implies that we expect the event to have canvas
182 * Can we guarantee that this is true?
185 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
187 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
188 bool const t = (*i)->motion_handler (e, from_autoscroll);
189 /* run all handlers; return true if at least one of them
190 returns true (indicating that the event has been handled).
202 DragManager::have_item (ArdourCanvas::Item* i) const
204 list<Drag*>::const_iterator j = _drags.begin ();
205 while (j != _drags.end() && (*j)->item () != i) {
209 return j != _drags.end ();
212 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
215 , _pointer_frame_offset (0)
216 , _trackview_only (trackview_only)
217 , _move_threshold_passed (false)
218 , _was_double_click (false)
219 , _raw_grab_frame (0)
221 , _last_pointer_frame (0)
227 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
240 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
242 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
244 if (Keyboard::is_button2_event (&event->button)) {
245 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
246 _y_constrained = true;
247 _x_constrained = false;
249 _y_constrained = false;
250 _x_constrained = true;
253 _x_constrained = false;
254 _y_constrained = false;
257 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
258 setup_pointer_frame_offset ();
259 _grab_frame = adjusted_frame (_raw_grab_frame, event);
260 _last_pointer_frame = _grab_frame;
261 _last_pointer_x = _grab_x;
263 if (_trackview_only) {
264 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
267 _last_pointer_y = _grab_y;
272 /* CAIROCANVAS need a variant here that passes *cursor */
274 _editor->push_canvas_cursor (cursor);
277 if (_editor->session() && _editor->session()->transport_rolling()) {
280 _was_rolling = false;
283 switch (_editor->snap_type()) {
284 case SnapToRegionStart:
285 case SnapToRegionEnd:
286 case SnapToRegionSync:
287 case SnapToRegionBoundary:
288 _editor->build_region_boundary_cache ();
295 /** Call to end a drag `successfully'. Ungrabs item and calls
296 * subclass' finished() method.
298 * @param event GDK event, or 0.
299 * @return true if some movement occurred, otherwise false.
302 Drag::end_grab (GdkEvent* event)
304 _editor->stop_canvas_autoscroll ();
308 finished (event, _move_threshold_passed);
310 _editor->verbose_cursor()->hide ();
311 _editor->pop_canvas_cursor ();
313 return _move_threshold_passed;
317 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
321 if (f > _pointer_frame_offset) {
322 pos = f - _pointer_frame_offset;
326 _editor->snap_to_with_modifier (pos, event);
333 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
335 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
339 Drag::current_pointer_y () const
341 if (!_trackview_only) {
342 return _drags->current_pointer_y ();
345 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
349 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
351 /* check to see if we have moved in any way that matters since the last motion event */
352 if (_move_threshold_passed &&
353 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
354 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
358 pair<framecnt_t, int> const threshold = move_threshold ();
360 bool const old_move_threshold_passed = _move_threshold_passed;
362 if (!from_autoscroll && !_move_threshold_passed) {
364 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
365 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
367 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
370 if (active (_editor->mouse_mode) && _move_threshold_passed) {
372 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
373 if (!from_autoscroll) {
374 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
377 if (!_editor->autoscroll_active() || from_autoscroll) {
378 motion (event, _move_threshold_passed != old_move_threshold_passed);
380 _last_pointer_x = _drags->current_pointer_x ();
381 _last_pointer_y = current_pointer_y ();
382 _last_pointer_frame = adjusted_current_frame (event);
392 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
400 aborted (_move_threshold_passed);
402 _editor->stop_canvas_autoscroll ();
403 _editor->verbose_cursor()->hide ();
407 Drag::show_verbose_cursor_time (framepos_t frame)
409 /* We use DragManager::current_pointer_y() here
410 because we need to position the verbose canvas
411 cursor within the overall canvas, regardless
412 of this particular drag's _trackview_only
416 _editor->verbose_cursor()->set_time (
418 _drags->current_pointer_x() + 10,
419 _drags->current_pointer_y() + 10
422 _editor->verbose_cursor()->show ();
426 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
428 _editor->verbose_cursor()->show (xoffset);
430 /* We use DragManager::current_pointer_y() here
431 because we need to position the verbose canvas
432 cursor within the overall canvas, regardless
433 of this particular drag's _trackview_only
437 _editor->verbose_cursor()->set_duration (
439 _drags->current_pointer_x() + 10,
440 _drags->current_pointer_y() + 10
445 Drag::show_verbose_cursor_text (string const & text)
447 _editor->verbose_cursor()->show ();
449 /* We use DragManager::current_pointer_y() here
450 because we need to position the verbose canvas
451 cursor within the overall canvas, regardless
452 of this particular drag's _trackview_only
456 _editor->verbose_cursor()->set (
458 _drags->current_pointer_x() + 10,
459 _drags->current_pointer_y() + 10
463 boost::shared_ptr<Region>
464 Drag::add_midi_region (MidiTimeAxisView* view)
466 if (_editor->session()) {
467 const TempoMap& map (_editor->session()->tempo_map());
468 framecnt_t pos = grab_frame();
469 const Meter& m = map.meter_at (pos);
470 /* not that the frame rate used here can be affected by pull up/down which
473 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
474 return view->add_region (grab_frame(), len, true);
477 return boost::shared_ptr<Region>();
480 struct EditorOrderTimeAxisViewSorter {
481 bool operator() (TimeAxisView* a, TimeAxisView* b) {
482 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
483 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
485 return ra->route()->order_key () < rb->route()->order_key ();
489 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
493 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
495 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
496 as some of the regions we are dragging may be on such tracks.
499 TrackViewList track_views = _editor->track_views;
500 track_views.sort (EditorOrderTimeAxisViewSorter ());
502 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
503 _time_axis_views.push_back (*i);
505 TimeAxisView::Children children_list = (*i)->get_child_list ();
506 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
507 _time_axis_views.push_back (j->get());
511 /* the list of views can be empty at this point if this is a region list-insert drag
514 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
515 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
518 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
522 RegionDrag::region_going_away (RegionView* v)
524 list<DraggingView>::iterator i = _views.begin ();
525 while (i != _views.end() && i->view != v) {
529 if (i != _views.end()) {
534 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
535 * or -1 if it is not found.
538 RegionDrag::find_time_axis_view (TimeAxisView* t) const
541 int const N = _time_axis_views.size ();
542 while (i < N && _time_axis_views[i] != t) {
553 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
554 : RegionDrag (e, i, p, v)
557 , _last_pointer_time_axis_view (0)
558 , _last_pointer_layer (0)
560 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
564 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
566 Drag::start_grab (event, cursor);
568 show_verbose_cursor_time (_last_frame_position);
570 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
572 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
573 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
578 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
580 /* compute the amount of pointer motion in frames, and where
581 the region would be if we moved it by that much.
583 *pending_region_position = adjusted_current_frame (event);
585 framepos_t sync_frame;
586 framecnt_t sync_offset;
589 sync_offset = _primary->region()->sync_offset (sync_dir);
591 /* we don't handle a sync point that lies before zero.
593 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
595 sync_frame = *pending_region_position + (sync_dir*sync_offset);
597 _editor->snap_to_with_modifier (sync_frame, event);
599 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
602 *pending_region_position = _last_frame_position;
605 if (*pending_region_position > max_framepos - _primary->region()->length()) {
606 *pending_region_position = _last_frame_position;
611 /* in locked edit mode, reverse the usual meaning of _x_constrained */
612 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
614 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
616 /* x movement since last time (in pixels) */
617 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
619 /* total x movement */
620 framecnt_t total_dx = *pending_region_position;
621 if (regions_came_from_canvas()) {
622 total_dx = total_dx - grab_frame ();
625 /* check that no regions have gone off the start of the session */
626 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
627 if ((i->view->region()->position() + total_dx) < 0) {
629 *pending_region_position = _last_frame_position;
634 _last_frame_position = *pending_region_position;
641 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
643 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
644 int const n = i->time_axis_view + delta_track;
645 if (n < 0 || n >= int (_time_axis_views.size())) {
646 /* off the top or bottom track */
650 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
651 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
652 /* not a track, or the wrong type */
656 double const l = i->layer + delta_layer;
658 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
659 mode to allow the user to place a region below another on layer 0.
661 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
662 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
663 If it has, the layers will be munged later anyway, so it's ok.
669 /* all regions being dragged are ok with this change */
674 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
676 double delta_layer = 0;
677 int delta_time_axis_view = 0;
679 assert (!_views.empty ());
681 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
683 /* Find the TimeAxisView that the pointer is now over */
684 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
685 TimeAxisView* tv = r.first;
687 if (tv && tv->view()) {
688 double layer = r.second;
690 if (first_move && tv->view()->layer_display() == Stacked) {
691 tv->view()->set_layer_display (Expanded);
694 /* Here's the current pointer position in terms of time axis view and layer */
695 int const current_pointer_time_axis_view = find_time_axis_view (tv);
696 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
698 /* Work out the change in y */
700 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
701 delta_layer = current_pointer_layer - _last_pointer_layer;
704 /* Work out the change in x */
705 framepos_t pending_region_position;
706 double const x_delta = compute_x_delta (event, &pending_region_position);
708 /* Verify change in y */
709 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
710 /* this y movement is not allowed, so do no y movement this time */
711 delta_time_axis_view = 0;
715 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
716 /* haven't reached next snap point, and we're not switching
717 trackviews nor layers. nothing to do.
722 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
724 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
726 RegionView* rv = i->view;
728 if (rv->region()->locked() || rv->region()->video_locked()) {
735 /* reparent the regionview into a group above all
739 ArdourCanvas::Item* rvg = rv->get_canvas_group();
740 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
741 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
742 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
743 /* move the item so that it continues to appear at the
744 same location now that its parent has changed.
746 rvg->move (rv_canvas_offset - dmg_canvas_offset);
749 /* If we have moved tracks, we'll fudge the layer delta so that the
750 region gets moved back onto layer 0 on its new track; this avoids
751 confusion when dragging regions from non-zero layers onto different
754 double this_delta_layer = delta_layer;
755 if (delta_time_axis_view != 0) {
756 this_delta_layer = - i->layer;
763 if (i->time_axis_view >= 0) {
764 track_index = i->time_axis_view + delta_time_axis_view;
766 track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
769 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
773 /* The TimeAxisView that this region is now over */
774 TimeAxisView* current_tv = _time_axis_views[track_index];
776 /* Ensure it is moved from stacked -> expanded if appropriate */
777 if (current_tv->view()->layer_display() == Stacked) {
778 current_tv->view()->set_layer_display (Expanded);
781 /* We're only allowed to go -ve in layer on Expanded views */
782 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
783 this_delta_layer = - i->layer;
787 rv->set_height (current_tv->view()->child_height ());
789 /* Update show/hidden status as the region view may have come from a hidden track,
790 or have moved to one.
792 if (current_tv->hidden ()) {
793 rv->get_canvas_group()->hide ();
795 rv->get_canvas_group()->show ();
798 /* Update the DraggingView */
799 i->time_axis_view = track_index;
800 i->layer += this_delta_layer;
803 _editor->mouse_brush_insert_region (rv, pending_region_position);
807 /* Get the y coordinate of the top of the track that this region is now over */
808 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
810 /* And adjust for the layer that it should be on */
811 StreamView* cv = current_tv->view ();
812 switch (cv->layer_display ()) {
816 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
819 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
823 /* need to get the parent of the regionview
824 * canvas group and get its position in
825 * equivalent coordinate space as the trackview
826 * we are now dragging over.
829 /* Now move the region view */
830 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->canvas_origin().y);
834 /* Only move the region into the empty dropzone at the bottom if the pointer
838 if (current_pointer_y() >= 0) {
840 Coord last_track_bottom_edge;
841 if (!_time_axis_views.empty()) {
842 TimeAxisView* last = _time_axis_views.back();
843 last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
845 last_track_bottom_edge = 0;
848 rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y);
849 i->time_axis_view = -1;
853 } /* foreach region */
855 _total_x_delta += x_delta;
857 if (x_delta != 0 && !_brushing) {
858 show_verbose_cursor_time (_last_frame_position);
861 _last_pointer_time_axis_view += delta_time_axis_view;
862 _last_pointer_layer += delta_layer;
866 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
868 if (_copy && first_move) {
870 /* duplicate the regionview(s) and region(s) */
872 list<DraggingView> new_regionviews;
874 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
876 RegionView* rv = i->view;
877 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
878 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
880 const boost::shared_ptr<const Region> original = rv->region();
881 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
882 region_copy->set_position (original->position());
886 boost::shared_ptr<AudioRegion> audioregion_copy
887 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
889 nrv = new AudioRegionView (*arv, audioregion_copy);
891 boost::shared_ptr<MidiRegion> midiregion_copy
892 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
893 nrv = new MidiRegionView (*mrv, midiregion_copy);
898 nrv->get_canvas_group()->show ();
899 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
901 /* swap _primary to the copy */
903 if (rv == _primary) {
907 /* ..and deselect the one we copied */
909 rv->set_selected (false);
912 if (!new_regionviews.empty()) {
914 /* reflect the fact that we are dragging the copies */
916 _views = new_regionviews;
918 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
922 RegionMotionDrag::motion (event, first_move);
926 RegionMotionDrag::finished (GdkEvent *, bool)
928 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
933 if ((*i)->view()->layer_display() == Expanded) {
934 (*i)->view()->set_layer_display (Stacked);
940 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
942 RegionMotionDrag::finished (ev, movement_occurred);
944 if (!movement_occurred) {
948 if (was_double_click() && !_views.empty()) {
949 DraggingView dv = _views.front();
950 dv.view->show_region_editor ();
957 /* reverse this here so that we have the correct logic to finalize
961 if (Config->get_edit_mode() == Lock) {
962 _x_constrained = !_x_constrained;
965 assert (!_views.empty ());
967 /* We might have hidden region views so that they weren't visible during the drag
968 (when they have been reparented). Now everything can be shown again, as region
969 views are back in their track parent groups.
971 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
972 i->view->get_canvas_group()->show ();
975 bool const changed_position = (_last_frame_position != _primary->region()->position());
976 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
977 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
997 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1001 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1003 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1008 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1009 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1010 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1011 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1013 rtav->set_height (original->current_height());
1017 ChanCount one_midi_port (DataType::MIDI, 1);
1018 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1019 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1020 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1022 rtav->set_height (original->current_height());
1027 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1033 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1035 RegionSelection new_views;
1036 PlaylistSet modified_playlists;
1037 RouteTimeAxisView* new_time_axis_view = 0;
1040 /* all changes were made during motion event handlers */
1042 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1046 _editor->commit_reversible_command ();
1050 if (_x_constrained) {
1051 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1053 _editor->begin_reversible_command (Operations::region_copy);
1056 /* insert the regions into their new playlists */
1057 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1059 RouteTimeAxisView* dest_rtv = 0;
1061 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1067 if (changed_position && !_x_constrained) {
1068 where = i->view->region()->position() - drag_delta;
1070 where = i->view->region()->position();
1073 if (i->time_axis_view < 0) {
1074 if (!new_time_axis_view) {
1075 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1077 dest_rtv = new_time_axis_view;
1079 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1082 if (dest_rtv != 0) {
1083 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1084 if (new_view != 0) {
1085 new_views.push_back (new_view);
1089 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1090 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1093 list<DraggingView>::const_iterator next = i;
1099 /* If we've created new regions either by copying or moving
1100 to a new track, we want to replace the old selection with the new ones
1103 if (new_views.size() > 0) {
1104 _editor->selection->set (new_views);
1107 /* write commands for the accumulated diffs for all our modified playlists */
1108 add_stateful_diff_commands_for_playlists (modified_playlists);
1110 _editor->commit_reversible_command ();
1114 RegionMoveDrag::finished_no_copy (
1115 bool const changed_position,
1116 bool const changed_tracks,
1117 framecnt_t const drag_delta
1120 RegionSelection new_views;
1121 PlaylistSet modified_playlists;
1122 PlaylistSet frozen_playlists;
1123 set<RouteTimeAxisView*> views_to_update;
1124 RouteTimeAxisView* new_time_axis_view = 0;
1127 /* all changes were made during motion event handlers */
1128 _editor->commit_reversible_command ();
1132 if (_x_constrained) {
1133 _editor->begin_reversible_command (_("fixed time region drag"));
1135 _editor->begin_reversible_command (Operations::region_drag);
1138 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1140 RegionView* rv = i->view;
1141 RouteTimeAxisView* dest_rtv = 0;
1143 if (rv->region()->locked() || rv->region()->video_locked()) {
1148 if (i->time_axis_view < 0) {
1149 if (!new_time_axis_view) {
1150 new_time_axis_view = create_destination_time_axis (rv->region(), i->initial_time_axis_view);
1152 dest_rtv = new_time_axis_view;
1154 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1159 double const dest_layer = i->layer;
1161 views_to_update.insert (dest_rtv);
1165 if (changed_position && !_x_constrained) {
1166 where = rv->region()->position() - drag_delta;
1168 where = rv->region()->position();
1171 if (changed_tracks) {
1173 /* insert into new playlist */
1175 RegionView* new_view = insert_region_into_playlist (
1176 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1179 if (new_view == 0) {
1184 new_views.push_back (new_view);
1186 /* remove from old playlist */
1188 /* the region that used to be in the old playlist is not
1189 moved to the new one - we use a copy of it. as a result,
1190 any existing editor for the region should no longer be
1193 rv->hide_region_editor();
1196 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1200 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1202 /* this movement may result in a crossfade being modified, or a layering change,
1203 so we need to get undo data from the playlist as well as the region.
1206 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1208 playlist->clear_changes ();
1211 rv->region()->clear_changes ();
1214 motion on the same track. plonk the previously reparented region
1215 back to its original canvas group (its streamview).
1216 No need to do anything for copies as they are fake regions which will be deleted.
1219 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1220 rv->get_canvas_group()->set_y_position (i->initial_y);
1223 /* just change the model */
1224 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1225 playlist->set_layer (rv->region(), dest_layer);
1228 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1230 r = frozen_playlists.insert (playlist);
1233 playlist->freeze ();
1236 rv->region()->set_position (where);
1238 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1241 if (changed_tracks) {
1243 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1244 was selected in all of them, then removing it from a playlist will have removed all
1245 trace of it from _views (i.e. there were N regions selected, we removed 1,
1246 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1247 corresponding regionview, and _views is now empty).
1249 This could have invalidated any and all iterators into _views.
1251 The heuristic we use here is: if the region selection is empty, break out of the loop
1252 here. if the region selection is not empty, then restart the loop because we know that
1253 we must have removed at least the region(view) we've just been working on as well as any
1254 that we processed on previous iterations.
1256 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1257 we can just iterate.
1261 if (_views.empty()) {
1272 /* If we've created new regions either by copying or moving
1273 to a new track, we want to replace the old selection with the new ones
1276 if (new_views.size() > 0) {
1277 _editor->selection->set (new_views);
1280 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1284 /* write commands for the accumulated diffs for all our modified playlists */
1285 add_stateful_diff_commands_for_playlists (modified_playlists);
1287 _editor->commit_reversible_command ();
1289 /* We have futzed with the layering of canvas items on our streamviews.
1290 If any region changed layer, this will have resulted in the stream
1291 views being asked to set up their region views, and all will be well.
1292 If not, we might now have badly-ordered region views. Ask the StreamViews
1293 involved to sort themselves out, just in case.
1296 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1297 (*i)->view()->playlist_layered ((*i)->track ());
1301 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1302 * @param region Region to remove.
1303 * @param playlist playlist To remove from.
1304 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1305 * that clear_changes () is only called once per playlist.
1308 RegionMoveDrag::remove_region_from_playlist (
1309 boost::shared_ptr<Region> region,
1310 boost::shared_ptr<Playlist> playlist,
1311 PlaylistSet& modified_playlists
1314 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1317 playlist->clear_changes ();
1320 playlist->remove_region (region);
1324 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1325 * clearing the playlist's diff history first if necessary.
1326 * @param region Region to insert.
1327 * @param dest_rtv Destination RouteTimeAxisView.
1328 * @param dest_layer Destination layer.
1329 * @param where Destination position.
1330 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1331 * that clear_changes () is only called once per playlist.
1332 * @return New RegionView, or 0 if no insert was performed.
1335 RegionMoveDrag::insert_region_into_playlist (
1336 boost::shared_ptr<Region> region,
1337 RouteTimeAxisView* dest_rtv,
1340 PlaylistSet& modified_playlists
1343 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1344 if (!dest_playlist) {
1348 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1349 _new_region_view = 0;
1350 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1352 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1353 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1355 dest_playlist->clear_changes ();
1358 dest_playlist->add_region (region, where);
1360 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1361 dest_playlist->set_layer (region, dest_layer);
1366 assert (_new_region_view);
1368 return _new_region_view;
1372 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1374 _new_region_view = rv;
1378 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1380 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1381 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1383 _editor->session()->add_command (c);
1392 RegionMoveDrag::aborted (bool movement_occurred)
1396 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1403 RegionMotionDrag::aborted (movement_occurred);
1408 RegionMotionDrag::aborted (bool)
1410 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1412 StreamView* sview = (*i)->view();
1415 if (sview->layer_display() == Expanded) {
1416 sview->set_layer_display (Stacked);
1421 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1422 RegionView* rv = i->view;
1423 TimeAxisView* tv = &(rv->get_time_axis_view ());
1424 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1426 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1427 rv->get_canvas_group()->set_y_position (0);
1429 rv->move (-_total_x_delta, 0);
1430 rv->set_height (rtv->view()->child_height ());
1434 /** @param b true to brush, otherwise false.
1435 * @param c true to make copies of the regions being moved, otherwise false.
1437 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1438 : RegionMotionDrag (e, i, p, v, b),
1441 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1444 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1445 if (rtv && rtv->is_track()) {
1446 speed = rtv->track()->speed ();
1449 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1453 RegionMoveDrag::setup_pointer_frame_offset ()
1455 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1458 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1459 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1461 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1463 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1464 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1466 _primary = v->view()->create_region_view (r, false, false);
1468 _primary->get_canvas_group()->show ();
1469 _primary->set_position (pos, 0);
1470 _views.push_back (DraggingView (_primary, this, v));
1472 _last_frame_position = pos;
1474 _item = _primary->get_canvas_group ();
1478 RegionInsertDrag::finished (GdkEvent *, bool)
1480 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1482 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1483 _primary->get_canvas_group()->set_y_position (0);
1485 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1487 _editor->begin_reversible_command (Operations::insert_region);
1488 playlist->clear_changes ();
1489 playlist->add_region (_primary->region (), _last_frame_position);
1490 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1491 _editor->commit_reversible_command ();
1499 RegionInsertDrag::aborted (bool)
1506 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1507 : RegionMoveDrag (e, i, p, v, false, false)
1509 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1512 struct RegionSelectionByPosition {
1513 bool operator() (RegionView*a, RegionView* b) {
1514 return a->region()->position () < b->region()->position();
1519 RegionSpliceDrag::motion (GdkEvent* event, bool)
1521 /* Which trackview is this ? */
1523 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1524 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1526 /* The region motion is only processed if the pointer is over
1530 if (!tv || !tv->is_track()) {
1531 /* To make sure we hide the verbose canvas cursor when the mouse is
1532 not held over and audiotrack.
1534 _editor->verbose_cursor()->hide ();
1540 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1546 RegionSelection copy (_editor->selection->regions);
1548 RegionSelectionByPosition cmp;
1551 framepos_t const pf = adjusted_current_frame (event);
1553 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1555 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1561 boost::shared_ptr<Playlist> playlist;
1563 if ((playlist = atv->playlist()) == 0) {
1567 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1572 if (pf < (*i)->region()->last_frame() + 1) {
1576 if (pf > (*i)->region()->first_frame()) {
1582 playlist->shuffle ((*i)->region(), dir);
1587 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1589 RegionMoveDrag::finished (event, movement_occurred);
1593 RegionSpliceDrag::aborted (bool)
1598 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1600 _view (dynamic_cast<MidiTimeAxisView*> (v))
1602 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1608 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1611 _region = add_midi_region (_view);
1612 _view->playlist()->freeze ();
1615 framepos_t const f = adjusted_current_frame (event);
1616 if (f < grab_frame()) {
1617 _region->set_position (f);
1620 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1621 so that if this region is duplicated, its duplicate starts on
1622 a snap point rather than 1 frame after a snap point. Otherwise things get
1623 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1624 place snapped notes at the start of the region.
1627 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1628 _region->set_length (len < 1 ? 1 : len);
1634 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1636 if (!movement_occurred) {
1637 add_midi_region (_view);
1639 _view->playlist()->thaw ();
1644 RegionCreateDrag::aborted (bool)
1647 _view->playlist()->thaw ();
1653 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1657 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1661 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1663 Gdk::Cursor* cursor;
1664 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1666 float x_fraction = cnote->mouse_x_fraction ();
1668 if (x_fraction > 0.0 && x_fraction < 0.25) {
1669 cursor = _editor->cursors()->left_side_trim;
1671 cursor = _editor->cursors()->right_side_trim;
1674 Drag::start_grab (event, cursor);
1676 region = &cnote->region_view();
1678 double const region_start = region->get_position_pixels();
1679 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1681 if (grab_x() <= middle_point) {
1682 cursor = _editor->cursors()->left_side_trim;
1685 cursor = _editor->cursors()->right_side_trim;
1691 if (event->motion.state & Keyboard::PrimaryModifier) {
1697 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1699 if (ms.size() > 1) {
1700 /* has to be relative, may make no sense otherwise */
1704 /* select this note; if it is already selected, preserve the existing selection,
1705 otherwise make this note the only one selected.
1707 region->note_selected (cnote, cnote->selected ());
1709 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1710 MidiRegionSelection::iterator next;
1713 (*r)->begin_resizing (at_front);
1719 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1721 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1722 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1723 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1725 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1730 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1732 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1733 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1734 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1736 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1741 NoteResizeDrag::aborted (bool)
1743 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1744 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1745 (*r)->abort_resizing ();
1749 AVDraggingView::AVDraggingView (RegionView* v)
1752 initial_position = v->region()->position ();
1755 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1758 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1761 TrackViewList empty;
1763 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1764 std::list<RegionView*> views = rs.by_layer();
1766 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1767 RegionView* rv = (*i);
1768 if (!rv->region()->video_locked()) {
1771 _views.push_back (AVDraggingView (rv));
1776 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1778 Drag::start_grab (event);
1779 if (_editor->session() == 0) {
1783 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1784 _max_backwards_drag = (
1785 ARDOUR_UI::instance()->video_timeline->get_duration()
1786 + ARDOUR_UI::instance()->video_timeline->get_offset()
1787 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1790 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1791 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1792 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1795 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1798 Timecode::Time timecode;
1799 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1800 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);
1801 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1802 _editor->verbose_cursor()->show ();
1806 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1808 if (_editor->session() == 0) {
1811 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1815 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1816 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
1818 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1819 dt = - _max_backwards_drag;
1822 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1823 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1825 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1826 RegionView* rv = i->view;
1827 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1830 rv->region()->clear_changes ();
1831 rv->region()->suspend_property_changes();
1833 rv->region()->set_position(i->initial_position + dt);
1834 rv->region_changed(ARDOUR::Properties::position);
1837 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1838 Timecode::Time timecode;
1839 Timecode::Time timediff;
1841 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1842 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1843 snprintf (buf, sizeof (buf),
1844 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1845 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1846 , _("Video Start:"),
1847 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1849 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1851 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1852 _editor->verbose_cursor()->show ();
1856 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1858 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1862 if (!movement_occurred || ! _editor->session()) {
1866 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1868 _editor->begin_reversible_command (_("Move Video"));
1870 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1871 ARDOUR_UI::instance()->video_timeline->save_undo();
1872 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1873 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1875 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1876 i->view->drag_end();
1877 i->view->region()->resume_property_changes ();
1879 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1882 _editor->session()->maybe_update_session_range(
1883 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1884 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1888 _editor->commit_reversible_command ();
1892 VideoTimeLineDrag::aborted (bool)
1894 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1897 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1898 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1900 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1901 i->view->region()->resume_property_changes ();
1902 i->view->region()->set_position(i->initial_position);
1906 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1907 : RegionDrag (e, i, p, v)
1908 , _preserve_fade_anchor (preserve_fade_anchor)
1909 , _jump_position_when_done (false)
1911 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1915 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1918 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1919 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1921 if (tv && tv->is_track()) {
1922 speed = tv->track()->speed();
1925 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1926 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1927 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1929 framepos_t const pf = adjusted_current_frame (event);
1931 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1932 /* Move the contents of the region around without changing the region bounds */
1933 _operation = ContentsTrim;
1934 Drag::start_grab (event, _editor->cursors()->trimmer);
1936 /* These will get overridden for a point trim.*/
1937 if (pf < (region_start + region_length/2)) {
1938 /* closer to front */
1939 _operation = StartTrim;
1941 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1942 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
1944 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1948 _operation = EndTrim;
1949 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1950 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
1952 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1957 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1958 _jump_position_when_done = true;
1961 switch (_operation) {
1963 show_verbose_cursor_time (region_start);
1964 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1965 i->view->trim_front_starting ();
1969 show_verbose_cursor_time (region_end);
1972 show_verbose_cursor_time (pf);
1976 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1977 i->view->region()->suspend_property_changes ();
1982 TrimDrag::motion (GdkEvent* event, bool first_move)
1984 RegionView* rv = _primary;
1987 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1988 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1989 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1990 frameoffset_t frame_delta = 0;
1992 if (tv && tv->is_track()) {
1993 speed = tv->track()->speed();
1996 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2002 switch (_operation) {
2004 trim_type = "Region start trim";
2007 trim_type = "Region end trim";
2010 trim_type = "Region content trim";
2017 _editor->begin_reversible_command (trim_type);
2019 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2020 RegionView* rv = i->view;
2021 rv->enable_display (false);
2022 rv->region()->playlist()->clear_owned_changes ();
2024 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2027 arv->temporarily_hide_envelope ();
2031 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2032 insert_result = _editor->motion_frozen_playlists.insert (pl);
2034 if (insert_result.second) {
2040 bool non_overlap_trim = false;
2042 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2043 non_overlap_trim = true;
2046 /* contstrain trim to fade length */
2047 if (_preserve_fade_anchor) {
2048 switch (_operation) {
2050 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2051 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2053 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2054 if (ar->locked()) continue;
2055 framecnt_t len = ar->fade_in()->back()->when;
2056 if (len < dt) dt = min(dt, len);
2060 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2061 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2063 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2064 if (ar->locked()) continue;
2065 framecnt_t len = ar->fade_out()->back()->when;
2066 if (len < -dt) dt = max(dt, -len);
2075 switch (_operation) {
2077 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2078 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2079 if (changed && _preserve_fade_anchor) {
2080 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2082 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2083 framecnt_t len = ar->fade_in()->back()->when;
2084 framecnt_t diff = ar->first_frame() - i->initial_position;
2085 framepos_t new_length = len - diff;
2086 i->anchored_fade_length = min (ar->length(), new_length);
2087 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2088 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2095 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2096 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2097 if (changed && _preserve_fade_anchor) {
2098 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2100 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2101 framecnt_t len = ar->fade_out()->back()->when;
2102 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2103 framepos_t new_length = len + diff;
2104 i->anchored_fade_length = min (ar->length(), new_length);
2105 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2106 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2114 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2116 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2117 i->view->move_contents (frame_delta);
2123 switch (_operation) {
2125 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2128 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2131 // show_verbose_cursor_time (frame_delta);
2138 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2140 if (movement_occurred) {
2141 motion (event, false);
2143 if (_operation == StartTrim) {
2144 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2146 /* This must happen before the region's StatefulDiffCommand is created, as it may
2147 `correct' (ahem) the region's _start from being negative to being zero. It
2148 needs to be zero in the undo record.
2150 i->view->trim_front_ending ();
2152 if (_preserve_fade_anchor) {
2153 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2155 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2156 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2157 ar->set_fade_in_length(i->anchored_fade_length);
2158 ar->set_fade_in_active(true);
2161 if (_jump_position_when_done) {
2162 i->view->region()->set_position (i->initial_position);
2165 } else if (_operation == EndTrim) {
2166 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2167 if (_preserve_fade_anchor) {
2168 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2170 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2171 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2172 ar->set_fade_out_length(i->anchored_fade_length);
2173 ar->set_fade_out_active(true);
2176 if (_jump_position_when_done) {
2177 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2182 if (!_views.empty()) {
2183 if (_operation == StartTrim) {
2184 _editor->maybe_locate_with_edit_preroll(
2185 _views.begin()->view->region()->position());
2187 if (_operation == EndTrim) {
2188 _editor->maybe_locate_with_edit_preroll(
2189 _views.begin()->view->region()->position() +
2190 _views.begin()->view->region()->length());
2194 if (!_editor->selection->selected (_primary)) {
2195 _primary->thaw_after_trim ();
2198 set<boost::shared_ptr<Playlist> > diffed_playlists;
2200 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2201 i->view->thaw_after_trim ();
2202 i->view->enable_display (true);
2204 /* Trimming one region may affect others on the playlist, so we need
2205 to get undo Commands from the whole playlist rather than just the
2206 region. Use diffed_playlists to make sure we don't diff a given
2207 playlist more than once.
2209 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2210 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2211 vector<Command*> cmds;
2213 _editor->session()->add_commands (cmds);
2214 diffed_playlists.insert (p);
2219 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2223 _editor->motion_frozen_playlists.clear ();
2224 _editor->commit_reversible_command();
2227 /* no mouse movement */
2228 _editor->point_trim (event, adjusted_current_frame (event));
2231 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2232 if (_operation == StartTrim) {
2233 i->view->trim_front_ending ();
2236 i->view->region()->resume_property_changes ();
2241 TrimDrag::aborted (bool movement_occurred)
2243 /* Our motion method is changing model state, so use the Undo system
2244 to cancel. Perhaps not ideal, as this will leave an Undo point
2245 behind which may be slightly odd from the user's point of view.
2250 if (movement_occurred) {
2254 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2255 i->view->region()->resume_property_changes ();
2260 TrimDrag::setup_pointer_frame_offset ()
2262 list<DraggingView>::iterator i = _views.begin ();
2263 while (i != _views.end() && i->view != _primary) {
2267 if (i == _views.end()) {
2271 switch (_operation) {
2273 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2276 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2283 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2287 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2288 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2293 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2295 Drag::start_grab (event, cursor);
2296 show_verbose_cursor_time (adjusted_current_frame(event));
2300 MeterMarkerDrag::setup_pointer_frame_offset ()
2302 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2306 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2308 if (!_marker->meter().movable()) {
2314 // create a dummy marker for visual representation of moving the
2315 // section, because whether its a copy or not, we're going to
2316 // leave or lose the original marker (leave if its a copy; lose if its
2317 // not, because we'll remove it from the map).
2319 MeterSection section (_marker->meter());
2321 if (!section.movable()) {
2326 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2328 _marker = new MeterMarker (
2330 *_editor->meter_group,
2331 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2333 *new MeterSection (_marker->meter())
2336 /* use the new marker for the grab */
2337 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2340 TempoMap& map (_editor->session()->tempo_map());
2341 /* get current state */
2342 before_state = &map.get_state();
2343 /* remove the section while we drag it */
2344 map.remove_meter (section, true);
2348 framepos_t const pf = adjusted_current_frame (event);
2349 _marker->set_position (pf);
2350 show_verbose_cursor_time (pf);
2354 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2356 if (!movement_occurred) {
2357 if (was_double_click()) {
2358 _editor->edit_meter_marker (*_marker);
2363 if (!_marker->meter().movable()) {
2367 motion (event, false);
2369 Timecode::BBT_Time when;
2371 TempoMap& map (_editor->session()->tempo_map());
2372 map.bbt_time (last_pointer_frame(), when);
2374 if (_copy == true) {
2375 _editor->begin_reversible_command (_("copy meter mark"));
2376 XMLNode &before = map.get_state();
2377 map.add_meter (_marker->meter(), when);
2378 XMLNode &after = map.get_state();
2379 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2380 _editor->commit_reversible_command ();
2383 _editor->begin_reversible_command (_("move meter mark"));
2385 /* we removed it before, so add it back now */
2387 map.add_meter (_marker->meter(), when);
2388 XMLNode &after = map.get_state();
2389 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2390 _editor->commit_reversible_command ();
2393 // delete the dummy marker we used for visual representation while moving.
2394 // a new visual marker will show up automatically.
2399 MeterMarkerDrag::aborted (bool moved)
2401 _marker->set_position (_marker->meter().frame ());
2404 TempoMap& map (_editor->session()->tempo_map());
2405 /* we removed it before, so add it back now */
2406 map.add_meter (_marker->meter(), _marker->meter().frame());
2407 // delete the dummy marker we used for visual representation while moving.
2408 // a new visual marker will show up automatically.
2413 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2417 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2419 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2424 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2426 Drag::start_grab (event, cursor);
2427 show_verbose_cursor_time (adjusted_current_frame (event));
2431 TempoMarkerDrag::setup_pointer_frame_offset ()
2433 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2437 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2439 if (!_marker->tempo().movable()) {
2445 // create a dummy marker for visual representation of moving the
2446 // section, because whether its a copy or not, we're going to
2447 // leave or lose the original marker (leave if its a copy; lose if its
2448 // not, because we'll remove it from the map).
2450 // create a dummy marker for visual representation of moving the copy.
2451 // The actual copying is not done before we reach the finish callback.
2454 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2456 TempoSection section (_marker->tempo());
2458 _marker = new TempoMarker (
2460 *_editor->tempo_group,
2461 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2463 *new TempoSection (_marker->tempo())
2466 /* use the new marker for the grab */
2467 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2470 TempoMap& map (_editor->session()->tempo_map());
2471 /* get current state */
2472 before_state = &map.get_state();
2473 /* remove the section while we drag it */
2474 map.remove_tempo (section, true);
2478 framepos_t const pf = adjusted_current_frame (event);
2479 _marker->set_position (pf);
2480 show_verbose_cursor_time (pf);
2484 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2486 if (!movement_occurred) {
2487 if (was_double_click()) {
2488 _editor->edit_tempo_marker (*_marker);
2493 if (!_marker->tempo().movable()) {
2497 motion (event, false);
2499 TempoMap& map (_editor->session()->tempo_map());
2500 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2501 Timecode::BBT_Time when;
2503 map.bbt_time (beat_time, when);
2505 if (_copy == true) {
2506 _editor->begin_reversible_command (_("copy tempo mark"));
2507 XMLNode &before = map.get_state();
2508 map.add_tempo (_marker->tempo(), when);
2509 XMLNode &after = map.get_state();
2510 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2511 _editor->commit_reversible_command ();
2514 _editor->begin_reversible_command (_("move tempo mark"));
2515 /* we removed it before, so add it back now */
2516 map.add_tempo (_marker->tempo(), when);
2517 XMLNode &after = map.get_state();
2518 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2519 _editor->commit_reversible_command ();
2522 // delete the dummy marker we used for visual representation while moving.
2523 // a new visual marker will show up automatically.
2528 TempoMarkerDrag::aborted (bool moved)
2530 _marker->set_position (_marker->tempo().frame());
2532 TempoMap& map (_editor->session()->tempo_map());
2533 /* we removed it before, so add it back now */
2534 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2535 // delete the dummy marker we used for visual representation while moving.
2536 // a new visual marker will show up automatically.
2541 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2542 : Drag (e, &c.track_canvas_item(), false)
2546 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2549 /** Do all the things we do when dragging the playhead to make it look as though
2550 * we have located, without actually doing the locate (because that would cause
2551 * the diskstream buffers to be refilled, which is too slow).
2554 CursorDrag::fake_locate (framepos_t t)
2556 _editor->playhead_cursor->set_position (t);
2558 Session* s = _editor->session ();
2559 if (s->timecode_transmission_suspended ()) {
2560 framepos_t const f = _editor->playhead_cursor->current_frame ();
2561 /* This is asynchronous so it will be sent "now"
2563 s->send_mmc_locate (f);
2564 /* These are synchronous and will be sent during the next
2567 s->queue_full_time_code ();
2568 s->queue_song_position_pointer ();
2571 show_verbose_cursor_time (t);
2572 _editor->UpdateAllTransportClocks (t);
2576 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2578 Drag::start_grab (event, c);
2580 _grab_zoom = _editor->samples_per_pixel;
2582 framepos_t where = _editor->canvas_event_sample (event);
2584 _editor->snap_to_with_modifier (where, event);
2586 _editor->_dragging_playhead = true;
2588 Session* s = _editor->session ();
2590 /* grab the track canvas item as well */
2592 _cursor.track_canvas_item().grab();
2595 if (_was_rolling && _stop) {
2599 if (s->is_auditioning()) {
2600 s->cancel_audition ();
2604 if (AudioEngine::instance()->connected()) {
2606 /* do this only if we're the engine is connected
2607 * because otherwise this request will never be
2608 * serviced and we'll busy wait forever. likewise,
2609 * notice if we are disconnected while waiting for the
2610 * request to be serviced.
2613 s->request_suspend_timecode_transmission ();
2614 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2615 /* twiddle our thumbs */
2620 fake_locate (where);
2624 CursorDrag::motion (GdkEvent* event, bool)
2626 framepos_t const adjusted_frame = adjusted_current_frame (event);
2627 if (adjusted_frame != last_pointer_frame()) {
2628 fake_locate (adjusted_frame);
2633 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2635 _editor->_dragging_playhead = false;
2637 _cursor.track_canvas_item().ungrab();
2639 if (!movement_occurred && _stop) {
2643 motion (event, false);
2645 Session* s = _editor->session ();
2647 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2648 _editor->_pending_locate_request = true;
2649 s->request_resume_timecode_transmission ();
2654 CursorDrag::aborted (bool)
2656 _cursor.track_canvas_item().ungrab();
2658 if (_editor->_dragging_playhead) {
2659 _editor->session()->request_resume_timecode_transmission ();
2660 _editor->_dragging_playhead = false;
2663 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2666 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2667 : RegionDrag (e, i, p, v)
2669 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2673 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2675 Drag::start_grab (event, cursor);
2677 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2678 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2680 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2684 FadeInDrag::setup_pointer_frame_offset ()
2686 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2687 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2688 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2692 FadeInDrag::motion (GdkEvent* event, bool)
2694 framecnt_t fade_length;
2696 framepos_t const pos = adjusted_current_frame (event);
2698 boost::shared_ptr<Region> region = _primary->region ();
2700 if (pos < (region->position() + 64)) {
2701 fade_length = 64; // this should be a minimum defined somewhere
2702 } else if (pos > region->last_frame()) {
2703 fade_length = region->length();
2705 fade_length = pos - region->position();
2708 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2710 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2716 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2719 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2723 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2725 if (!movement_occurred) {
2729 framecnt_t fade_length;
2731 framepos_t const pos = adjusted_current_frame (event);
2733 boost::shared_ptr<Region> region = _primary->region ();
2735 if (pos < (region->position() + 64)) {
2736 fade_length = 64; // this should be a minimum defined somewhere
2737 } else if (pos > region->last_frame()) {
2738 fade_length = region->length();
2740 fade_length = pos - region->position();
2743 _editor->begin_reversible_command (_("change fade in length"));
2745 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2747 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2753 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2754 XMLNode &before = alist->get_state();
2756 tmp->audio_region()->set_fade_in_length (fade_length);
2757 tmp->audio_region()->set_fade_in_active (true);
2759 XMLNode &after = alist->get_state();
2760 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2763 _editor->commit_reversible_command ();
2767 FadeInDrag::aborted (bool)
2769 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2770 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2776 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2780 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2781 : RegionDrag (e, i, p, v)
2783 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2787 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2789 Drag::start_grab (event, cursor);
2791 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2792 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2794 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2798 FadeOutDrag::setup_pointer_frame_offset ()
2800 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2801 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2802 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2806 FadeOutDrag::motion (GdkEvent* event, bool)
2808 framecnt_t fade_length;
2810 framepos_t const pos = adjusted_current_frame (event);
2812 boost::shared_ptr<Region> region = _primary->region ();
2814 if (pos > (region->last_frame() - 64)) {
2815 fade_length = 64; // this should really be a minimum fade defined somewhere
2817 else if (pos < region->position()) {
2818 fade_length = region->length();
2821 fade_length = region->last_frame() - pos;
2824 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2826 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2832 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2835 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2839 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2841 if (!movement_occurred) {
2845 framecnt_t fade_length;
2847 framepos_t const pos = adjusted_current_frame (event);
2849 boost::shared_ptr<Region> region = _primary->region ();
2851 if (pos > (region->last_frame() - 64)) {
2852 fade_length = 64; // this should really be a minimum fade defined somewhere
2854 else if (pos < region->position()) {
2855 fade_length = region->length();
2858 fade_length = region->last_frame() - pos;
2861 _editor->begin_reversible_command (_("change fade out length"));
2863 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2865 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2871 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2872 XMLNode &before = alist->get_state();
2874 tmp->audio_region()->set_fade_out_length (fade_length);
2875 tmp->audio_region()->set_fade_out_active (true);
2877 XMLNode &after = alist->get_state();
2878 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2881 _editor->commit_reversible_command ();
2885 FadeOutDrag::aborted (bool)
2887 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2888 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2894 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2898 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2901 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2903 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2906 _points.push_back (ArdourCanvas::Duple (0, 0));
2907 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2910 MarkerDrag::~MarkerDrag ()
2912 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2917 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2919 location = new Location (*l);
2920 markers.push_back (m);
2925 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2927 Drag::start_grab (event, cursor);
2931 Location *location = _editor->find_location_from_marker (_marker, is_start);
2932 _editor->_dragging_edit_point = true;
2934 update_item (location);
2936 // _drag_line->show();
2937 // _line->raise_to_top();
2940 show_verbose_cursor_time (location->start());
2942 show_verbose_cursor_time (location->end());
2945 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2948 case Selection::Toggle:
2949 /* we toggle on the button release */
2951 case Selection::Set:
2952 if (!_editor->selection->selected (_marker)) {
2953 _editor->selection->set (_marker);
2956 case Selection::Extend:
2958 Locations::LocationList ll;
2959 list<Marker*> to_add;
2961 _editor->selection->markers.range (s, e);
2962 s = min (_marker->position(), s);
2963 e = max (_marker->position(), e);
2966 if (e < max_framepos) {
2969 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2970 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2971 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2974 to_add.push_back (lm->start);
2977 to_add.push_back (lm->end);
2981 if (!to_add.empty()) {
2982 _editor->selection->add (to_add);
2986 case Selection::Add:
2987 _editor->selection->add (_marker);
2991 /* Set up copies for us to manipulate during the drag
2994 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2996 Location* l = _editor->find_location_from_marker (*i, is_start);
3003 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3005 /* range: check that the other end of the range isn't
3008 CopiedLocationInfo::iterator x;
3009 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3010 if (*(*x).location == *l) {
3014 if (x == _copied_locations.end()) {
3015 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3017 (*x).markers.push_back (*i);
3018 (*x).move_both = true;
3026 MarkerDrag::setup_pointer_frame_offset ()
3029 Location *location = _editor->find_location_from_marker (_marker, is_start);
3030 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3034 MarkerDrag::motion (GdkEvent* event, bool)
3036 framecnt_t f_delta = 0;
3038 bool move_both = false;
3039 Location *real_location;
3040 Location *copy_location = 0;
3042 framepos_t const newframe = adjusted_current_frame (event);
3043 framepos_t next = newframe;
3045 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3049 CopiedLocationInfo::iterator x;
3051 /* find the marker we're dragging, and compute the delta */
3053 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3055 copy_location = (*x).location;
3057 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3059 /* this marker is represented by this
3060 * CopiedLocationMarkerInfo
3063 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3068 if (real_location->is_mark()) {
3069 f_delta = newframe - copy_location->start();
3073 switch (_marker->type()) {
3074 case Marker::SessionStart:
3075 case Marker::RangeStart:
3076 case Marker::LoopStart:
3077 case Marker::PunchIn:
3078 f_delta = newframe - copy_location->start();
3081 case Marker::SessionEnd:
3082 case Marker::RangeEnd:
3083 case Marker::LoopEnd:
3084 case Marker::PunchOut:
3085 f_delta = newframe - copy_location->end();
3088 /* what kind of marker is this ? */
3097 if (x == _copied_locations.end()) {
3098 /* hmm, impossible - we didn't find the dragged marker */
3102 /* now move them all */
3104 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3106 copy_location = x->location;
3108 /* call this to find out if its the start or end */
3110 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3114 if (real_location->locked()) {
3118 if (copy_location->is_mark()) {
3122 copy_location->set_start (copy_location->start() + f_delta);
3126 framepos_t new_start = copy_location->start() + f_delta;
3127 framepos_t new_end = copy_location->end() + f_delta;
3129 if (is_start) { // start-of-range marker
3131 if (move_both || (*x).move_both) {
3132 copy_location->set_start (new_start);
3133 copy_location->set_end (new_end);
3134 } else if (new_start < copy_location->end()) {
3135 copy_location->set_start (new_start);
3136 } else if (newframe > 0) {
3137 _editor->snap_to (next, 1, true);
3138 copy_location->set_end (next);
3139 copy_location->set_start (newframe);
3142 } else { // end marker
3144 if (move_both || (*x).move_both) {
3145 copy_location->set_end (new_end);
3146 copy_location->set_start (new_start);
3147 } else if (new_end > copy_location->start()) {
3148 copy_location->set_end (new_end);
3149 } else if (newframe > 0) {
3150 _editor->snap_to (next, -1, true);
3151 copy_location->set_start (next);
3152 copy_location->set_end (newframe);
3157 update_item (copy_location);
3159 /* now lookup the actual GUI items used to display this
3160 * location and move them to wherever the copy of the location
3161 * is now. This means that the logic in ARDOUR::Location is
3162 * still enforced, even though we are not (yet) modifying
3163 * the real Location itself.
3166 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3169 lm->set_position (copy_location->start(), copy_location->end());
3174 assert (!_copied_locations.empty());
3176 show_verbose_cursor_time (newframe);
3180 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3182 if (!movement_occurred) {
3184 if (was_double_click()) {
3185 _editor->rename_marker (_marker);
3189 /* just a click, do nothing but finish
3190 off the selection process
3193 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3196 case Selection::Set:
3197 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3198 _editor->selection->set (_marker);
3202 case Selection::Toggle:
3203 /* we toggle on the button release, click only */
3204 _editor->selection->toggle (_marker);
3207 case Selection::Extend:
3208 case Selection::Add:
3215 _editor->_dragging_edit_point = false;
3217 _editor->begin_reversible_command ( _("move marker") );
3218 XMLNode &before = _editor->session()->locations()->get_state();
3220 MarkerSelection::iterator i;
3221 CopiedLocationInfo::iterator x;
3224 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3225 x != _copied_locations.end() && i != _editor->selection->markers.end();
3228 Location * location = _editor->find_location_from_marker (*i, is_start);
3232 if (location->locked()) {
3236 if (location->is_mark()) {
3237 location->set_start (((*x).location)->start());
3239 location->set (((*x).location)->start(), ((*x).location)->end());
3244 XMLNode &after = _editor->session()->locations()->get_state();
3245 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3246 _editor->commit_reversible_command ();
3250 MarkerDrag::aborted (bool)
3256 MarkerDrag::update_item (Location*)
3261 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3263 _cumulative_x_drag (0),
3264 _cumulative_y_drag (0)
3266 if (_zero_gain_fraction < 0.0) {
3267 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3270 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3272 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3278 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3280 Drag::start_grab (event, _editor->cursors()->fader);
3282 // start the grab at the center of the control point so
3283 // the point doesn't 'jump' to the mouse after the first drag
3284 _fixed_grab_x = _point->get_x();
3285 _fixed_grab_y = _point->get_y();
3287 float const fraction = 1 - (_point->get_y() / _point->line().height());
3289 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3291 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3292 event->button.x + 10, event->button.y + 10);
3294 _editor->verbose_cursor()->show ();
3296 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3298 if (!_point->can_slide ()) {
3299 _x_constrained = true;
3304 ControlPointDrag::motion (GdkEvent* event, bool)
3306 double dx = _drags->current_pointer_x() - last_pointer_x();
3307 double dy = current_pointer_y() - last_pointer_y();
3309 if (event->button.state & Keyboard::SecondaryModifier) {
3314 /* coordinate in pixels relative to the start of the region (for region-based automation)
3315 or track (for track-based automation) */
3316 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3317 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3319 // calculate zero crossing point. back off by .01 to stay on the
3320 // positive side of zero
3321 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3323 // make sure we hit zero when passing through
3324 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3328 if (_x_constrained) {
3331 if (_y_constrained) {
3335 _cumulative_x_drag = cx - _fixed_grab_x;
3336 _cumulative_y_drag = cy - _fixed_grab_y;
3340 cy = min ((double) _point->line().height(), cy);
3342 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3344 if (!_x_constrained) {
3345 _editor->snap_to_with_modifier (cx_frames, event);
3348 cx_frames = min (cx_frames, _point->line().maximum_time());
3350 float const fraction = 1.0 - (cy / _point->line().height());
3352 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3354 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3358 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3360 if (!movement_occurred) {
3364 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3365 _editor->reset_point_selection ();
3369 motion (event, false);
3372 _point->line().end_drag (_pushing, _final_index);
3373 _editor->session()->commit_reversible_command ();
3377 ControlPointDrag::aborted (bool)
3379 _point->line().reset ();
3383 ControlPointDrag::active (Editing::MouseMode m)
3385 if (m == Editing::MouseGain) {
3386 /* always active in mouse gain */
3390 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3391 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3394 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3397 _cumulative_y_drag (0)
3399 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3403 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3405 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3408 _item = &_line->grab_item ();
3410 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3411 origin, and ditto for y.
3414 double cx = event->button.x;
3415 double cy = event->button.y;
3417 _line->parent_group().canvas_to_item (cx, cy);
3419 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3424 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3425 /* no adjacent points */
3429 Drag::start_grab (event, _editor->cursors()->fader);
3431 /* store grab start in parent frame */
3436 double fraction = 1.0 - (cy / _line->height());
3438 _line->start_drag_line (before, after, fraction);
3440 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3441 event->button.x + 10, event->button.y + 10);
3443 _editor->verbose_cursor()->show ();
3447 LineDrag::motion (GdkEvent* event, bool)
3449 double dy = current_pointer_y() - last_pointer_y();
3451 if (event->button.state & Keyboard::SecondaryModifier) {
3455 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3457 _cumulative_y_drag = cy - _fixed_grab_y;
3460 cy = min ((double) _line->height(), cy);
3462 double const fraction = 1.0 - (cy / _line->height());
3465 /* we are ignoring x position for this drag, so we can just pass in anything */
3466 _line->drag_motion (0, fraction, true, false, ignored);
3468 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3472 LineDrag::finished (GdkEvent* event, bool movement_occured)
3474 if (movement_occured) {
3475 motion (event, false);
3476 _line->end_drag (false, 0);
3478 /* add a new control point on the line */
3480 AutomationTimeAxisView* atv;
3482 _line->end_drag (false, 0);
3484 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3485 framepos_t where = _editor->window_event_sample (event, 0, 0);
3486 atv->add_automation_event (event, where, event->button.y, false);
3490 _editor->session()->commit_reversible_command ();
3494 LineDrag::aborted (bool)
3499 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3502 _cumulative_x_drag (0)
3504 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3508 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3510 Drag::start_grab (event);
3512 _line = reinterpret_cast<Line*> (_item);
3515 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3517 double cx = event->button.x;
3518 double cy = event->button.y;
3520 _item->parent()->canvas_to_item (cx, cy);
3522 /* store grab start in parent frame */
3523 _region_view_grab_x = cx;
3525 _before = *(float*) _item->get_data ("position");
3527 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3529 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3533 FeatureLineDrag::motion (GdkEvent*, bool)
3535 double dx = _drags->current_pointer_x() - last_pointer_x();
3537 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3539 _cumulative_x_drag += dx;
3541 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3550 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3552 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3554 float *pos = new float;
3557 _line->set_data ("position", pos);
3563 FeatureLineDrag::finished (GdkEvent*, bool)
3565 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3566 _arv->update_transient(_before, _before);
3570 FeatureLineDrag::aborted (bool)
3575 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3577 , _vertical_only (false)
3579 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3583 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3585 Drag::start_grab (event);
3586 show_verbose_cursor_time (adjusted_current_frame (event));
3590 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3597 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3599 framepos_t grab = grab_frame ();
3600 if (Config->get_rubberbanding_snaps_to_grid ()) {
3601 _editor->snap_to_with_modifier (grab, event);
3604 /* base start and end on initial click position */
3614 if (current_pointer_y() < grab_y()) {
3615 y1 = current_pointer_y();
3618 y2 = current_pointer_y();
3622 if (start != end || y1 != y2) {
3624 double x1 = _editor->sample_to_pixel (start);
3625 double x2 = _editor->sample_to_pixel (end);
3626 const double min_dimension = 2.0;
3628 if (_vertical_only) {
3629 /* fixed 10 pixel width */
3633 x2 = min (x1 - min_dimension, x2);
3635 x2 = max (x1 + min_dimension, x2);
3640 y2 = min (y1 - min_dimension, y2);
3642 y2 = max (y1 + min_dimension, y2);
3645 /* translate rect into item space and set */
3647 ArdourCanvas::Rect r (x1, y1, x2, y2);
3649 /* this drag is a _trackview_only == true drag, so the y1 and
3650 * y2 (computed using current_pointer_y() and grab_y()) will be
3651 * relative to the top of the trackview group). The
3652 * rubberband rect has the same parent/scroll offset as the
3653 * the trackview group, so we can use the "r" rect directly
3654 * to set the shape of the rubberband.
3657 _editor->rubberband_rect->set (r);
3658 _editor->rubberband_rect->show();
3659 _editor->rubberband_rect->raise_to_top();
3661 show_verbose_cursor_time (pf);
3663 do_select_things (event, true);
3668 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3673 if (grab_frame() < last_pointer_frame()) {
3675 x2 = last_pointer_frame ();
3678 x1 = last_pointer_frame ();
3684 if (current_pointer_y() < grab_y()) {
3685 y1 = current_pointer_y();
3688 y2 = current_pointer_y();
3692 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3696 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3698 if (movement_occurred) {
3700 motion (event, false);
3701 do_select_things (event, false);
3707 bool do_deselect = true;
3708 MidiTimeAxisView* mtv;
3710 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3712 if (_editor->selection->empty()) {
3713 /* nothing selected */
3714 add_midi_region (mtv);
3715 do_deselect = false;
3719 /* do not deselect if Primary or Tertiary (toggle-select or
3720 * extend-select are pressed.
3723 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3724 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3731 _editor->rubberband_rect->hide();
3735 RubberbandSelectDrag::aborted (bool)
3737 _editor->rubberband_rect->hide ();
3740 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3741 : RegionDrag (e, i, p, v)
3743 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3747 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3749 Drag::start_grab (event, cursor);
3751 show_verbose_cursor_time (adjusted_current_frame (event));
3755 TimeFXDrag::motion (GdkEvent* event, bool)
3757 RegionView* rv = _primary;
3758 StreamView* cv = rv->get_time_axis_view().view ();
3760 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3761 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3762 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3764 framepos_t const pf = adjusted_current_frame (event);
3766 if (pf > rv->region()->position()) {
3767 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3770 show_verbose_cursor_time (pf);
3774 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3776 _primary->get_time_axis_view().hide_timestretch ();
3778 if (!movement_occurred) {
3782 if (last_pointer_frame() < _primary->region()->position()) {
3783 /* backwards drag of the left edge - not usable */
3787 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3789 float percentage = (double) newlen / (double) _primary->region()->length();
3791 #ifndef USE_RUBBERBAND
3792 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3793 if (_primary->region()->data_type() == DataType::AUDIO) {
3794 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3798 if (!_editor->get_selection().regions.empty()) {
3799 /* primary will already be included in the selection, and edit
3800 group shared editing will propagate selection across
3801 equivalent regions, so just use the current region
3805 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3806 error << _("An error occurred while executing time stretch operation") << endmsg;
3812 TimeFXDrag::aborted (bool)
3814 _primary->get_time_axis_view().hide_timestretch ();
3817 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3820 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3824 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3826 Drag::start_grab (event);
3830 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3832 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3836 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3838 if (movement_occurred && _editor->session()) {
3839 /* make sure we stop */
3840 _editor->session()->request_transport_speed (0.0);
3845 ScrubDrag::aborted (bool)
3850 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3855 , _original_pointer_time_axis (-1)
3856 , _last_pointer_time_axis (-1)
3857 , _time_selection_at_start (!_editor->get_selection().time.empty())
3859 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3861 if (_time_selection_at_start) {
3862 start_at_start = _editor->get_selection().time.start();
3863 end_at_start = _editor->get_selection().time.end_frame();
3868 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3870 if (_editor->session() == 0) {
3874 Gdk::Cursor* cursor = 0;
3876 switch (_operation) {
3877 case CreateSelection:
3878 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3883 cursor = _editor->cursors()->selector;
3884 Drag::start_grab (event, cursor);
3887 case SelectionStartTrim:
3888 if (_editor->clicked_axisview) {
3889 _editor->clicked_axisview->order_selection_trims (_item, true);
3891 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3894 case SelectionEndTrim:
3895 if (_editor->clicked_axisview) {
3896 _editor->clicked_axisview->order_selection_trims (_item, false);
3898 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3902 Drag::start_grab (event, cursor);
3905 case SelectionExtend:
3906 Drag::start_grab (event, cursor);
3910 if (_operation == SelectionMove) {
3911 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3913 show_verbose_cursor_time (adjusted_current_frame (event));
3916 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
3920 SelectionDrag::setup_pointer_frame_offset ()
3922 switch (_operation) {
3923 case CreateSelection:
3924 _pointer_frame_offset = 0;
3927 case SelectionStartTrim:
3929 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3932 case SelectionEndTrim:
3933 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3936 case SelectionExtend:
3942 SelectionDrag::motion (GdkEvent* event, bool first_move)
3944 framepos_t start = 0;
3946 framecnt_t length = 0;
3947 framecnt_t distance = 0;
3949 framepos_t const pending_position = adjusted_current_frame (event);
3951 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
3955 switch (_operation) {
3956 case CreateSelection:
3958 framepos_t grab = grab_frame ();
3961 grab = adjusted_current_frame (event, false);
3962 if (grab < pending_position) {
3963 _editor->snap_to (grab, -1);
3965 _editor->snap_to (grab, 1);
3969 if (pending_position < grab) {
3970 start = pending_position;
3973 end = pending_position;
3977 /* first drag: Either add to the selection
3978 or create a new selection
3985 /* adding to the selection */
3986 _editor->set_selected_track_as_side_effect (Selection::Add);
3987 _editor->clicked_selection = _editor->selection->add (start, end);
3994 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3995 _editor->set_selected_track_as_side_effect (Selection::Set);
3998 _editor->clicked_selection = _editor->selection->set (start, end);
4002 /* select all tracks within the rectangle that we've marked out so far */
4003 TrackViewList to_be_added_to_selection;
4004 TrackViewList to_be_removed_from_selection;
4005 TrackViewList& all_tracks (_editor->track_views);
4007 ArdourCanvas::Coord const top = grab_y();
4008 ArdourCanvas::Coord const bottom = current_pointer_y();
4010 if (top >= 0 && bottom >= 0) {
4012 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4014 if ((*i)->covered_by_y_range (top, bottom)) {
4015 if (!(*i)->get_selected()) {
4016 to_be_added_to_selection.push_back (*i);
4019 if ((*i)->get_selected()) {
4020 to_be_removed_from_selection.push_back (*i);
4025 if (!to_be_added_to_selection.empty()) {
4026 _editor->selection->add (to_be_added_to_selection);
4029 if (!to_be_removed_from_selection.empty()) {
4030 _editor->selection->remove (to_be_removed_from_selection);
4036 case SelectionStartTrim:
4038 start = _editor->selection->time[_editor->clicked_selection].start;
4039 end = _editor->selection->time[_editor->clicked_selection].end;
4041 if (pending_position > end) {
4044 start = pending_position;
4048 case SelectionEndTrim:
4050 start = _editor->selection->time[_editor->clicked_selection].start;
4051 end = _editor->selection->time[_editor->clicked_selection].end;
4053 if (pending_position < start) {
4056 end = pending_position;
4063 start = _editor->selection->time[_editor->clicked_selection].start;
4064 end = _editor->selection->time[_editor->clicked_selection].end;
4066 length = end - start;
4067 distance = pending_position - start;
4068 start = pending_position;
4069 _editor->snap_to (start);
4071 end = start + length;
4075 case SelectionExtend:
4080 switch (_operation) {
4082 if (_time_selection_at_start) {
4083 _editor->selection->move_time (distance);
4087 _editor->selection->replace (_editor->clicked_selection, start, end);
4091 if (_operation == SelectionMove) {
4092 show_verbose_cursor_time(start);
4094 show_verbose_cursor_time(pending_position);
4099 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4101 Session* s = _editor->session();
4103 if (movement_occurred) {
4104 motion (event, false);
4105 /* XXX this is not object-oriented programming at all. ick */
4106 if (_editor->selection->time.consolidate()) {
4107 _editor->selection->TimeChanged ();
4110 /* XXX what if its a music time selection? */
4112 if ( s->get_play_range() && s->transport_rolling() ) {
4113 s->request_play_range (&_editor->selection->time, true);
4115 if (Config->get_always_play_range() && !s->transport_rolling()) {
4116 s->request_locate (_editor->get_selection().time.start());
4122 /* just a click, no pointer movement.
4125 if (_operation == SelectionExtend) {
4126 if (_time_selection_at_start) {
4127 framepos_t pos = adjusted_current_frame (event, false);
4128 framepos_t start = min (pos, start_at_start);
4129 framepos_t end = max (pos, end_at_start);
4130 _editor->selection->set (start, end);
4133 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4134 if (_editor->clicked_selection) {
4135 _editor->selection->remove (_editor->clicked_selection);
4138 if (!_editor->clicked_selection) {
4139 _editor->selection->clear_time();
4144 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4145 _editor->selection->set (_editor->clicked_axisview);
4148 if (s && s->get_play_range () && s->transport_rolling()) {
4149 s->request_stop (false, false);
4154 _editor->stop_canvas_autoscroll ();
4155 _editor->clicked_selection = 0;
4159 SelectionDrag::aborted (bool)
4164 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4165 : Drag (e, i, false),
4169 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4171 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4172 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4173 physical_screen_height (_editor->get_window())));
4174 _drag_rect->hide ();
4176 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4177 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4181 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4183 if (_editor->session() == 0) {
4187 Gdk::Cursor* cursor = 0;
4189 if (!_editor->temp_location) {
4190 _editor->temp_location = new Location (*_editor->session());
4193 switch (_operation) {
4194 case CreateRangeMarker:
4195 case CreateTransportMarker:
4196 case CreateCDMarker:
4198 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4203 cursor = _editor->cursors()->selector;
4207 Drag::start_grab (event, cursor);
4209 show_verbose_cursor_time (adjusted_current_frame (event));
4213 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4215 framepos_t start = 0;
4217 ArdourCanvas::Rectangle *crect;
4219 switch (_operation) {
4220 case CreateRangeMarker:
4221 crect = _editor->range_bar_drag_rect;
4223 case CreateTransportMarker:
4224 crect = _editor->transport_bar_drag_rect;
4226 case CreateCDMarker:
4227 crect = _editor->cd_marker_bar_drag_rect;
4230 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4235 framepos_t const pf = adjusted_current_frame (event);
4237 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4238 framepos_t grab = grab_frame ();
4239 _editor->snap_to (grab);
4241 if (pf < grab_frame()) {
4249 /* first drag: Either add to the selection
4250 or create a new selection.
4255 _editor->temp_location->set (start, end);
4259 update_item (_editor->temp_location);
4261 //_drag_rect->raise_to_top();
4267 _editor->temp_location->set (start, end);
4269 double x1 = _editor->sample_to_pixel (start);
4270 double x2 = _editor->sample_to_pixel (end);
4274 update_item (_editor->temp_location);
4277 show_verbose_cursor_time (pf);
4282 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4284 Location * newloc = 0;
4288 if (movement_occurred) {
4289 motion (event, false);
4292 switch (_operation) {
4293 case CreateRangeMarker:
4294 case CreateCDMarker:
4296 _editor->begin_reversible_command (_("new range marker"));
4297 XMLNode &before = _editor->session()->locations()->get_state();
4298 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4299 if (_operation == CreateCDMarker) {
4300 flags = Location::IsRangeMarker | Location::IsCDMarker;
4301 _editor->cd_marker_bar_drag_rect->hide();
4304 flags = Location::IsRangeMarker;
4305 _editor->range_bar_drag_rect->hide();
4307 newloc = new Location (
4308 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4311 _editor->session()->locations()->add (newloc, true);
4312 XMLNode &after = _editor->session()->locations()->get_state();
4313 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4314 _editor->commit_reversible_command ();
4318 case CreateTransportMarker:
4319 // popup menu to pick loop or punch
4320 _editor->new_transport_marker_context_menu (&event->button, _item);
4326 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4328 if (_operation == CreateTransportMarker) {
4330 /* didn't drag, so just locate */
4332 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4334 } else if (_operation == CreateCDMarker) {
4336 /* didn't drag, but mark is already created so do
4339 } else { /* operation == CreateRangeMarker */
4345 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4347 if (end == max_framepos) {
4348 end = _editor->session()->current_end_frame ();
4351 if (start == max_framepos) {
4352 start = _editor->session()->current_start_frame ();
4355 switch (_editor->mouse_mode) {
4357 /* find the two markers on either side and then make the selection from it */
4358 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4362 /* find the two markers on either side of the click and make the range out of it */
4363 _editor->selection->set (start, end);
4372 _editor->stop_canvas_autoscroll ();
4376 RangeMarkerBarDrag::aborted (bool)
4382 RangeMarkerBarDrag::update_item (Location* location)
4384 double const x1 = _editor->sample_to_pixel (location->start());
4385 double const x2 = _editor->sample_to_pixel (location->end());
4387 _drag_rect->set_x0 (x1);
4388 _drag_rect->set_x1 (x2);
4391 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4395 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4399 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4401 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4402 Drag::start_grab (event, _editor->cursors()->zoom_out);
4405 Drag::start_grab (event, _editor->cursors()->zoom_in);
4409 show_verbose_cursor_time (adjusted_current_frame (event));
4413 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4418 framepos_t const pf = adjusted_current_frame (event);
4420 framepos_t grab = grab_frame ();
4421 _editor->snap_to_with_modifier (grab, event);
4423 /* base start and end on initial click position */
4435 _editor->zoom_rect->show();
4436 _editor->zoom_rect->raise_to_top();
4439 _editor->reposition_zoom_rect(start, end);
4441 show_verbose_cursor_time (pf);
4446 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4448 if (movement_occurred) {
4449 motion (event, false);
4451 if (grab_frame() < last_pointer_frame()) {
4452 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4454 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4457 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4458 _editor->tav_zoom_step (_zoom_out);
4460 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4464 _editor->zoom_rect->hide();
4468 MouseZoomDrag::aborted (bool)
4470 _editor->zoom_rect->hide ();
4473 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4475 , _cumulative_dx (0)
4476 , _cumulative_dy (0)
4478 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4480 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4482 _region = &_primary->region_view ();
4483 _note_height = _region->midi_stream_view()->note_height ();
4487 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4489 Drag::start_grab (event);
4491 if (!(_was_selected = _primary->selected())) {
4493 /* tertiary-click means extend selection - we'll do that on button release,
4494 so don't add it here, because otherwise we make it hard to figure
4495 out the "extend-to" range.
4498 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4501 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4504 _region->note_selected (_primary, true);
4506 _region->unique_select (_primary);
4512 /** @return Current total drag x change in frames */
4514 NoteDrag::total_dx () const
4517 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4519 /* primary note time */
4520 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4522 /* new time of the primary note in session frames */
4523 frameoffset_t st = n + dx;
4525 framepos_t const rp = _region->region()->position ();
4527 /* prevent the note being dragged earlier than the region's position */
4530 /* snap and return corresponding delta */
4531 return _region->snap_frame_to_frame (st - rp) + rp - n;
4534 /** @return Current total drag y change in note number */
4536 NoteDrag::total_dy () const
4538 MidiStreamView* msv = _region->midi_stream_view ();
4539 double const y = _region->midi_view()->y_position ();
4540 /* new current note */
4541 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4543 n = max (msv->lowest_note(), n);
4544 n = min (msv->highest_note(), n);
4545 /* and work out delta */
4546 return n - msv->y_to_note (grab_y() - y);
4550 NoteDrag::motion (GdkEvent *, bool)
4552 /* Total change in x and y since the start of the drag */
4553 frameoffset_t const dx = total_dx ();
4554 int8_t const dy = total_dy ();
4556 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4557 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4558 double const tdy = -dy * _note_height - _cumulative_dy;
4561 _cumulative_dx += tdx;
4562 _cumulative_dy += tdy;
4564 int8_t note_delta = total_dy();
4566 _region->move_selection (tdx, tdy, note_delta);
4568 /* the new note value may be the same as the old one, but we
4569 * don't know what that means because the selection may have
4570 * involved more than one note and we might be doing something
4571 * odd with them. so show the note value anyway, always.
4575 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4577 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4578 (int) floor ((double)new_note));
4580 show_verbose_cursor_text (buf);
4585 NoteDrag::finished (GdkEvent* ev, bool moved)
4588 /* no motion - select note */
4590 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4591 _editor->current_mouse_mode() == Editing::MouseDraw) {
4593 if (_was_selected) {
4594 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4596 _region->note_deselected (_primary);
4599 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4600 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4602 if (!extend && !add && _region->selection_size() > 1) {
4603 _region->unique_select (_primary);
4604 } else if (extend) {
4605 _region->note_selected (_primary, true, true);
4607 /* it was added during button press */
4612 _region->note_dropped (_primary, total_dx(), total_dy());
4617 NoteDrag::aborted (bool)
4622 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4623 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4624 : Drag (editor, atv->base_item ())
4626 , _nothing_to_drag (false)
4628 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4629 y_origin = atv->y_position();
4630 setup (atv->lines ());
4633 /** Make an AutomationRangeDrag for region gain lines */
4634 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4635 : Drag (editor, rv->get_canvas_group ())
4637 , _nothing_to_drag (false)
4639 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4641 list<boost::shared_ptr<AutomationLine> > lines;
4642 lines.push_back (rv->get_gain_line ());
4643 y_origin = rv->get_time_axis_view().y_position();
4647 /** @param lines AutomationLines to drag.
4648 * @param offset Offset from the session start to the points in the AutomationLines.
4651 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4653 /* find the lines that overlap the ranges being dragged */
4654 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4655 while (i != lines.end ()) {
4656 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4659 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4661 /* check this range against all the AudioRanges that we are using */
4662 list<AudioRange>::const_iterator k = _ranges.begin ();
4663 while (k != _ranges.end()) {
4664 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4670 /* add it to our list if it overlaps at all */
4671 if (k != _ranges.end()) {
4676 _lines.push_back (n);
4682 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4686 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4688 return 1.0 - ((global_y - y_origin) / line->height());
4692 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4694 Drag::start_grab (event, cursor);
4696 /* Get line states before we start changing things */
4697 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4698 i->state = &i->line->get_state ();
4699 i->original_fraction = y_fraction (i->line, current_pointer_y());
4702 if (_ranges.empty()) {
4704 /* No selected time ranges: drag all points */
4705 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4706 uint32_t const N = i->line->npoints ();
4707 for (uint32_t j = 0; j < N; ++j) {
4708 i->points.push_back (i->line->nth (j));
4714 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4716 framecnt_t const half = (i->start + i->end) / 2;
4718 /* find the line that this audio range starts in */
4719 list<Line>::iterator j = _lines.begin();
4720 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4724 if (j != _lines.end()) {
4725 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4727 /* j is the line that this audio range starts in; fade into it;
4728 64 samples length plucked out of thin air.
4731 framepos_t a = i->start + 64;
4736 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4737 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4739 the_list->add (p, the_list->eval (p));
4740 the_list->add (q, the_list->eval (q));
4743 /* same thing for the end */
4746 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4750 if (j != _lines.end()) {
4751 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4753 /* j is the line that this audio range starts in; fade out of it;
4754 64 samples length plucked out of thin air.
4757 framepos_t b = i->end - 64;
4762 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4763 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4765 the_list->add (p, the_list->eval (p));
4766 the_list->add (q, the_list->eval (q));
4770 _nothing_to_drag = true;
4772 /* Find all the points that should be dragged and put them in the relevant
4773 points lists in the Line structs.
4776 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4778 uint32_t const N = i->line->npoints ();
4779 for (uint32_t j = 0; j < N; ++j) {
4781 /* here's a control point on this line */
4782 ControlPoint* p = i->line->nth (j);
4783 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4785 /* see if it's inside a range */
4786 list<AudioRange>::const_iterator k = _ranges.begin ();
4787 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4791 if (k != _ranges.end()) {
4792 /* dragging this point */
4793 _nothing_to_drag = false;
4794 i->points.push_back (p);
4800 if (_nothing_to_drag) {
4804 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4805 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
4810 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4812 if (_nothing_to_drag) {
4816 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4817 float const f = y_fraction (l->line, current_pointer_y());
4818 /* we are ignoring x position for this drag, so we can just pass in anything */
4820 l->line->drag_motion (0, f, true, false, ignored);
4821 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4826 AutomationRangeDrag::finished (GdkEvent* event, bool)
4828 if (_nothing_to_drag) {
4832 motion (event, false);
4833 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4834 i->line->end_drag (false, 0);
4837 _editor->session()->commit_reversible_command ();
4841 AutomationRangeDrag::aborted (bool)
4843 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4848 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
4850 , initial_time_axis_view (itav)
4852 /* note that time_axis_view may be null if the regionview was created
4853 * as part of a copy operation.
4855 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4856 layer = v->region()->layer ();
4857 initial_y = v->get_canvas_group()->position().y;
4858 initial_playlist = v->region()->playlist ();
4859 initial_position = v->region()->position ();
4860 initial_end = v->region()->position () + v->region()->length ();
4863 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4864 : Drag (e, i->canvas_item ())
4867 , _cumulative_dx (0)
4869 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4870 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4875 PatchChangeDrag::motion (GdkEvent* ev, bool)
4877 framepos_t f = adjusted_current_frame (ev);
4878 boost::shared_ptr<Region> r = _region_view->region ();
4879 f = max (f, r->position ());
4880 f = min (f, r->last_frame ());
4882 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4883 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4884 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4885 _cumulative_dx = dxu;
4889 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4891 if (!movement_occurred) {
4895 boost::shared_ptr<Region> r (_region_view->region ());
4896 framepos_t f = adjusted_current_frame (ev);
4897 f = max (f, r->position ());
4898 f = min (f, r->last_frame ());
4900 _region_view->move_patch_change (
4902 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4907 PatchChangeDrag::aborted (bool)
4909 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4913 PatchChangeDrag::setup_pointer_frame_offset ()
4915 boost::shared_ptr<Region> region = _region_view->region ();
4916 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4919 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4920 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4927 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4929 framepos_t const p = _region_view->region()->position ();
4930 double const y = _region_view->midi_view()->y_position ();
4932 x1 = max ((framepos_t) 0, x1 - p);
4933 x2 = max ((framepos_t) 0, x2 - p);
4934 y1 = max (0.0, y1 - y);
4935 y2 = max (0.0, y2 - y);
4937 _region_view->update_drag_selection (
4938 _editor->sample_to_pixel (x1),
4939 _editor->sample_to_pixel (x2),
4942 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4947 MidiRubberbandSelectDrag::deselect_things ()
4952 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4953 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4956 _vertical_only = true;
4960 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4962 double const y = _region_view->midi_view()->y_position ();
4964 y1 = max (0.0, y1 - y);
4965 y2 = max (0.0, y2 - y);
4967 _region_view->update_vertical_drag_selection (
4970 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4975 MidiVerticalSelectDrag::deselect_things ()
4980 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4981 : RubberbandSelectDrag (e, i)
4987 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4989 if (drag_in_progress) {
4990 /* We just want to select things at the end of the drag, not during it */
4994 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4996 _editor->begin_reversible_command (_("rubberband selection"));
4997 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4998 _editor->commit_reversible_command ();
5002 EditorRubberbandSelectDrag::deselect_things ()
5004 if (!getenv("ARDOUR_SAE")) {
5005 _editor->selection->clear_tracks();
5007 _editor->selection->clear_regions();
5008 _editor->selection->clear_points ();
5009 _editor->selection->clear_lines ();
5012 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5020 NoteCreateDrag::~NoteCreateDrag ()
5026 NoteCreateDrag::grid_frames (framepos_t t) const
5029 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
5034 return _region_view->region_beats_to_region_frames (grid_beats);
5038 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5040 Drag::start_grab (event, cursor);
5042 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5044 framepos_t pf = _drags->current_pointer_frame ();
5045 framecnt_t const g = grid_frames (pf);
5047 /* Hack so that we always snap to the note that we are over, instead of snapping
5048 to the next one if we're more than halfway through the one we're over.
5050 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5054 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5056 MidiStreamView* sv = _region_view->midi_stream_view ();
5057 double const x = _editor->sample_to_pixel (_note[0]);
5058 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5060 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5061 _drag_rect->set_outline_all ();
5062 _drag_rect->set_outline_color (0xffffff99);
5063 _drag_rect->set_fill_color (0xffffff66);
5067 NoteCreateDrag::motion (GdkEvent* event, bool)
5069 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5070 double const x = _editor->sample_to_pixel (_note[1]);
5071 if (_note[1] > _note[0]) {
5072 _drag_rect->set_x1 (x);
5074 _drag_rect->set_x0 (x);
5079 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5081 if (!had_movement) {
5085 framepos_t const start = min (_note[0], _note[1]);
5086 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5088 framecnt_t const g = grid_frames (start);
5089 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5091 if (_editor->snap_mode() == SnapNormal && length < g) {
5092 length = g - one_tick;
5095 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5097 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5101 NoteCreateDrag::y_to_region (double y) const
5104 _region_view->get_canvas_group()->canvas_to_item (x, y);
5109 NoteCreateDrag::aborted (bool)
5114 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5119 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5123 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5125 Drag::start_grab (event, cursor);
5129 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5135 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5138 distance = _drags->current_pointer_x() - grab_x();
5139 len = ar->fade_in()->back()->when;
5141 distance = grab_x() - _drags->current_pointer_x();
5142 len = ar->fade_out()->back()->when;
5145 /* how long should it be ? */
5147 new_length = len + _editor->pixel_to_sample (distance);
5149 /* now check with the region that this is legal */
5151 new_length = ar->verify_xfade_bounds (new_length, start);
5154 arv->reset_fade_in_shape_width (ar, new_length);
5156 arv->reset_fade_out_shape_width (ar, new_length);
5161 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5167 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5170 distance = _drags->current_pointer_x() - grab_x();
5171 len = ar->fade_in()->back()->when;
5173 distance = grab_x() - _drags->current_pointer_x();
5174 len = ar->fade_out()->back()->when;
5177 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5179 _editor->begin_reversible_command ("xfade trim");
5180 ar->playlist()->clear_owned_changes ();
5183 ar->set_fade_in_length (new_length);
5185 ar->set_fade_out_length (new_length);
5188 /* Adjusting the xfade may affect other regions in the playlist, so we need
5189 to get undo Commands from the whole playlist rather than just the
5193 vector<Command*> cmds;
5194 ar->playlist()->rdiff (cmds);
5195 _editor->session()->add_commands (cmds);
5196 _editor->commit_reversible_command ();
5201 CrossfadeEdgeDrag::aborted (bool)
5204 arv->redraw_start_xfade ();
5206 arv->redraw_end_xfade ();