2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/scroll_group.h"
48 #include "audio_region_view.h"
49 #include "midi_region_view.h"
50 #include "ardour_ui.h"
51 #include "gui_thread.h"
52 #include "control_point.h"
54 #include "region_gain_line.h"
55 #include "editor_drag.h"
56 #include "audio_time_axis.h"
57 #include "midi_time_axis.h"
58 #include "selection.h"
59 #include "midi_selection.h"
60 #include "automation_time_axis.h"
62 #include "editor_cursors.h"
63 #include "mouse_cursors.h"
64 #include "note_base.h"
65 #include "patch_change.h"
66 #include "verbose_cursor.h"
69 using namespace ARDOUR;
72 using namespace Gtkmm2ext;
73 using namespace Editing;
74 using namespace ArdourCanvas;
76 using Gtkmm2ext::Keyboard;
78 double ControlPointDrag::_zero_gain_fraction = -1.0;
80 DragManager::DragManager (Editor* e)
83 , _current_pointer_frame (0)
87 DragManager::~DragManager ()
92 /** Call abort for each active drag */
98 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
103 if (!_drags.empty ()) {
104 _editor->set_follow_playhead (_old_follow_playhead, false);
113 DragManager::add (Drag* d)
115 d->set_manager (this);
116 _drags.push_back (d);
120 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
122 d->set_manager (this);
123 _drags.push_back (d);
128 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
130 /* Prevent follow playhead during the drag to be nice to the user */
131 _old_follow_playhead = _editor->follow_playhead ();
132 _editor->set_follow_playhead (false);
134 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
136 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
137 (*i)->start_grab (e, c);
141 /** Call end_grab for each active drag.
142 * @return true if any drag reported movement having occurred.
145 DragManager::end_grab (GdkEvent* e)
150 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
151 bool const t = (*i)->end_grab (e);
162 _editor->set_follow_playhead (_old_follow_playhead, false);
168 DragManager::mark_double_click ()
170 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
171 (*i)->set_double_click (true);
176 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
180 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
182 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
183 bool const t = (*i)->motion_handler (e, from_autoscroll);
194 DragManager::window_motion_handler (GdkEvent* e, bool from_autoscroll)
198 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
200 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
201 bool const t = (*i)->motion_handler (e, from_autoscroll);
212 DragManager::have_item (ArdourCanvas::Item* i) const
214 list<Drag*>::const_iterator j = _drags.begin ();
215 while (j != _drags.end() && (*j)->item () != i) {
219 return j != _drags.end ();
222 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
225 , _pointer_frame_offset (0)
226 , _move_threshold_passed (false)
227 , _was_double_click (false)
228 , _raw_grab_frame (0)
230 , _last_pointer_frame (0)
236 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
249 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
251 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
253 if (Keyboard::is_button2_event (&event->button)) {
254 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
255 _y_constrained = true;
256 _x_constrained = false;
258 _y_constrained = false;
259 _x_constrained = true;
262 _x_constrained = false;
263 _y_constrained = false;
266 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
267 setup_pointer_frame_offset ();
268 _grab_frame = adjusted_frame (_raw_grab_frame, event);
269 _last_pointer_frame = _grab_frame;
270 _last_pointer_x = _grab_x;
271 _last_pointer_y = _grab_y;
277 /* CAIROCANVAS need a variant here that passes *cursor */
282 if (_editor->session() && _editor->session()->transport_rolling()) {
285 _was_rolling = false;
288 switch (_editor->snap_type()) {
289 case SnapToRegionStart:
290 case SnapToRegionEnd:
291 case SnapToRegionSync:
292 case SnapToRegionBoundary:
293 _editor->build_region_boundary_cache ();
300 /** Call to end a drag `successfully'. Ungrabs item and calls
301 * subclass' finished() method.
303 * @param event GDK event, or 0.
304 * @return true if some movement occurred, otherwise false.
307 Drag::end_grab (GdkEvent* event)
309 _editor->stop_canvas_autoscroll ();
313 finished (event, _move_threshold_passed);
315 _editor->verbose_cursor()->hide ();
317 return _move_threshold_passed;
321 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
325 if (f > _pointer_frame_offset) {
326 pos = f - _pointer_frame_offset;
330 _editor->snap_to_with_modifier (pos, event);
337 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
339 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
343 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
345 /* check to see if we have moved in any way that matters since the last motion event */
346 if (_move_threshold_passed &&
347 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
348 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
352 pair<framecnt_t, int> const threshold = move_threshold ();
354 bool const old_move_threshold_passed = _move_threshold_passed;
356 if (!from_autoscroll && !_move_threshold_passed) {
358 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
359 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
361 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
364 if (active (_editor->mouse_mode) && _move_threshold_passed) {
366 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
367 if (!from_autoscroll) {
368 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
371 if (!_editor->autoscroll_active() || from_autoscroll) {
372 motion (event, _move_threshold_passed != old_move_threshold_passed);
374 _last_pointer_x = _drags->current_pointer_x ();
375 _last_pointer_y = _drags->current_pointer_y ();
376 _last_pointer_frame = adjusted_current_frame (event);
385 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
393 aborted (_move_threshold_passed);
395 _editor->stop_canvas_autoscroll ();
396 _editor->verbose_cursor()->hide ();
400 Drag::show_verbose_cursor_time (framepos_t frame)
402 _editor->verbose_cursor()->set_time (
404 _drags->current_pointer_x() + 10,
405 _drags->current_pointer_y() + 10
408 _editor->verbose_cursor()->show ();
412 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
414 _editor->verbose_cursor()->show (xoffset);
416 _editor->verbose_cursor()->set_duration (
418 _drags->current_pointer_x() + 10,
419 _drags->current_pointer_y() + 10
424 Drag::show_verbose_cursor_text (string const & text)
426 _editor->verbose_cursor()->show ();
428 _editor->verbose_cursor()->set (
430 _drags->current_pointer_x() + 10,
431 _drags->current_pointer_y() + 10
435 boost::shared_ptr<Region>
436 Drag::add_midi_region (MidiTimeAxisView* view)
438 if (_editor->session()) {
439 const TempoMap& map (_editor->session()->tempo_map());
440 framecnt_t pos = grab_frame();
441 const Meter& m = map.meter_at (pos);
442 /* not that the frame rate used here can be affected by pull up/down which
445 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
446 return view->add_region (grab_frame(), len, true);
449 return boost::shared_ptr<Region>();
452 struct EditorOrderTimeAxisViewSorter {
453 bool operator() (TimeAxisView* a, TimeAxisView* b) {
454 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
455 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
457 return ra->route()->order_key () < rb->route()->order_key ();
461 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
465 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
467 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
468 as some of the regions we are dragging may be on such tracks.
471 TrackViewList track_views = _editor->track_views;
472 track_views.sort (EditorOrderTimeAxisViewSorter ());
474 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
475 _time_axis_views.push_back (*i);
477 TimeAxisView::Children children_list = (*i)->get_child_list ();
478 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
479 _time_axis_views.push_back (j->get());
483 /* the list of views can be empty at this point if this is a region list-insert drag
486 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
487 _views.push_back (DraggingView (*i, this));
490 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
494 RegionDrag::region_going_away (RegionView* v)
496 list<DraggingView>::iterator i = _views.begin ();
497 while (i != _views.end() && i->view != v) {
501 if (i != _views.end()) {
506 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
507 * or -1 if it is not found.
510 RegionDrag::find_time_axis_view (TimeAxisView* t) const
513 int const N = _time_axis_views.size ();
514 while (i < N && _time_axis_views[i] != t) {
525 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
526 : RegionDrag (e, i, p, v)
529 , _last_pointer_time_axis_view (0)
530 , _last_pointer_layer (0)
532 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
536 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
538 Drag::start_grab (event, cursor);
540 show_verbose_cursor_time (_last_frame_position);
542 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
544 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
545 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
550 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
552 /* compute the amount of pointer motion in frames, and where
553 the region would be if we moved it by that much.
555 *pending_region_position = adjusted_current_frame (event);
557 framepos_t sync_frame;
558 framecnt_t sync_offset;
561 sync_offset = _primary->region()->sync_offset (sync_dir);
563 /* we don't handle a sync point that lies before zero.
565 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
567 sync_frame = *pending_region_position + (sync_dir*sync_offset);
569 _editor->snap_to_with_modifier (sync_frame, event);
571 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
574 *pending_region_position = _last_frame_position;
577 if (*pending_region_position > max_framepos - _primary->region()->length()) {
578 *pending_region_position = _last_frame_position;
583 /* in locked edit mode, reverse the usual meaning of _x_constrained */
584 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
586 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
588 /* x movement since last time (in pixels) */
589 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
591 /* total x movement */
592 framecnt_t total_dx = *pending_region_position;
593 if (regions_came_from_canvas()) {
594 total_dx = total_dx - grab_frame ();
597 /* check that no regions have gone off the start of the session */
598 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
599 if ((i->view->region()->position() + total_dx) < 0) {
601 *pending_region_position = _last_frame_position;
606 _last_frame_position = *pending_region_position;
613 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
615 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
616 int const n = i->time_axis_view + delta_track;
617 if (n < 0 || n >= int (_time_axis_views.size())) {
618 /* off the top or bottom track */
622 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
623 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
624 /* not a track, or the wrong type */
628 double const l = i->layer + delta_layer;
630 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
631 mode to allow the user to place a region below another on layer 0.
633 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
634 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
635 If it has, the layers will be munged later anyway, so it's ok.
641 /* all regions being dragged are ok with this change */
646 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
648 double delta_layer = 0;
649 int delta_time_axis_view = 0;
651 assert (!_views.empty ());
653 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
655 /* Find the TimeAxisView that the pointer is now over */
656 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (_drags->current_pointer_y ());
657 TimeAxisView* tv = r.first;
659 if (tv && tv->view()) {
660 double layer = r.second;
662 if (first_move && tv->view()->layer_display() == Stacked) {
663 tv->view()->set_layer_display (Expanded);
666 /* Here's the current pointer position in terms of time axis view and layer */
667 int const current_pointer_time_axis_view = find_time_axis_view (tv);
668 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
670 /* Work out the change in y */
672 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
673 delta_layer = current_pointer_layer - _last_pointer_layer;
676 /* Work out the change in x */
677 framepos_t pending_region_position;
678 double const x_delta = compute_x_delta (event, &pending_region_position);
680 /* Verify change in y */
681 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
682 /* this y movement is not allowed, so do no y movement this time */
683 delta_time_axis_view = 0;
687 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
688 /* haven't reached next snap point, and we're not switching
689 trackviews nor layers. nothing to do.
694 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
696 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
698 RegionView* rv = i->view;
700 if (rv->region()->locked() || rv->region()->video_locked()) {
706 rv->fake_set_opaque (true);
708 /* reparent the regionview into a group above all
712 ArdourCanvas::Group* rvg = rv->get_canvas_group();
713 Duple rv_canvas_offset = rvg->parent()->item_to_canvas (Duple (0,0));
714 Duple dmg_canvas_offset = _editor->_drag_motion_group->item_to_canvas (Duple (0,0));
715 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
716 rvg->move (rv_canvas_offset - dmg_canvas_offset);
719 /* If we have moved tracks, we'll fudge the layer delta so that the
720 region gets moved back onto layer 0 on its new track; this avoids
721 confusion when dragging regions from non-zero layers onto different
724 double this_delta_layer = delta_layer;
725 if (delta_time_axis_view != 0) {
726 this_delta_layer = - i->layer;
731 int track_index = i->time_axis_view + delta_time_axis_view;
733 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
737 /* The TimeAxisView that this region is now over */
738 TimeAxisView* current_tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
740 /* Ensure it is moved from stacked -> expanded if appropriate */
741 if (current_tv->view()->layer_display() == Stacked) {
742 current_tv->view()->set_layer_display (Expanded);
745 /* We're only allowed to go -ve in layer on Expanded views */
746 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
747 this_delta_layer = - i->layer;
751 rv->set_height (current_tv->view()->child_height ());
753 /* Update show/hidden status as the region view may have come from a hidden track,
754 or have moved to one.
756 if (current_tv->hidden ()) {
757 rv->get_canvas_group()->hide ();
759 rv->get_canvas_group()->show ();
762 /* Update the DraggingView */
763 i->time_axis_view += delta_time_axis_view;
764 i->layer += this_delta_layer;
767 _editor->mouse_brush_insert_region (rv, pending_region_position);
771 /* Get the y coordinate of the top of the track that this region is now over */
772 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
774 /* And adjust for the layer that it should be on */
775 StreamView* cv = current_tv->view ();
776 switch (cv->layer_display ()) {
780 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
783 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
787 /* need to get the parent of the regionview
788 * canvas group and get its position in
789 * equivalent coordinate space as the trackview
790 * we are now dragging over.
793 /* Now move the region view */
794 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->item_to_canvas (Duple (0, 0)).y);
798 /* Only move the region into the empty dropzone at the bottom if the pointer
802 if (_drags->current_pointer_y() >= _editor->get_trackview_group()->item_to_canvas (Duple (0,0)).y) {
805 TimeAxisView* last = _time_axis_views.back();
806 track_origin = last->canvas_display()->item_to_canvas (track_origin);
807 track_origin.y += last->effective_height();
808 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->item_to_canvas (Duple (0,0)).y);
809 i->time_axis_view = -1;
813 } /* foreach region */
815 _total_x_delta += x_delta;
817 if (x_delta != 0 && !_brushing) {
818 show_verbose_cursor_time (_last_frame_position);
821 _last_pointer_time_axis_view += delta_time_axis_view;
822 _last_pointer_layer += delta_layer;
826 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
828 if (_copy && first_move) {
830 /* duplicate the regionview(s) and region(s) */
832 list<DraggingView> new_regionviews;
834 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
836 RegionView* rv = i->view;
837 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
838 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
840 const boost::shared_ptr<const Region> original = rv->region();
841 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
842 region_copy->set_position (original->position());
846 boost::shared_ptr<AudioRegion> audioregion_copy
847 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
849 nrv = new AudioRegionView (*arv, audioregion_copy);
851 boost::shared_ptr<MidiRegion> midiregion_copy
852 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
853 nrv = new MidiRegionView (*mrv, midiregion_copy);
858 nrv->get_canvas_group()->show ();
859 new_regionviews.push_back (DraggingView (nrv, this));
861 /* swap _primary to the copy */
863 if (rv == _primary) {
867 /* ..and deselect the one we copied */
869 rv->set_selected (false);
872 if (!new_regionviews.empty()) {
874 /* reflect the fact that we are dragging the copies */
876 _views = new_regionviews;
878 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
882 RegionMotionDrag::motion (event, first_move);
886 RegionMotionDrag::finished (GdkEvent *, bool)
888 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
893 if ((*i)->view()->layer_display() == Expanded) {
894 (*i)->view()->set_layer_display (Stacked);
900 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
902 RegionMotionDrag::finished (ev, movement_occurred);
904 if (!movement_occurred) {
908 if (was_double_click() && !_views.empty()) {
909 DraggingView dv = _views.front();
910 dv.view->show_region_editor ();
917 /* reverse this here so that we have the correct logic to finalize
921 if (Config->get_edit_mode() == Lock) {
922 _x_constrained = !_x_constrained;
925 assert (!_views.empty ());
927 /* We might have hidden region views so that they weren't visible during the drag
928 (when they have been reparented). Now everything can be shown again, as region
929 views are back in their track parent groups.
931 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
932 i->view->get_canvas_group()->show ();
935 bool const changed_position = (_last_frame_position != _primary->region()->position());
936 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
937 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
957 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
961 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region)
963 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
968 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
969 list<boost::shared_ptr<AudioTrack> > audio_tracks;
970 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
971 return _editor->axis_view_from_route (audio_tracks.front());
973 ChanCount one_midi_port (DataType::MIDI, 1);
974 list<boost::shared_ptr<MidiTrack> > midi_tracks;
975 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
976 return _editor->axis_view_from_route (midi_tracks.front());
979 error << _("Could not create new track after region placed in the drop zone") << endmsg;
985 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
987 RegionSelection new_views;
988 PlaylistSet modified_playlists;
989 RouteTimeAxisView* new_time_axis_view = 0;
992 /* all changes were made during motion event handlers */
994 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
998 _editor->commit_reversible_command ();
1002 if (_x_constrained) {
1003 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1005 _editor->begin_reversible_command (Operations::region_copy);
1008 /* insert the regions into their new playlists */
1009 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1011 RouteTimeAxisView* dest_rtv = 0;
1013 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1019 if (changed_position && !_x_constrained) {
1020 where = i->view->region()->position() - drag_delta;
1022 where = i->view->region()->position();
1025 if (i->time_axis_view < 0) {
1026 if (!new_time_axis_view) {
1027 new_time_axis_view = create_destination_time_axis (i->view->region());
1029 dest_rtv = new_time_axis_view;
1031 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1034 if (dest_rtv != 0) {
1035 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1036 if (new_view != 0) {
1037 new_views.push_back (new_view);
1041 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1042 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1045 list<DraggingView>::const_iterator next = i;
1051 /* If we've created new regions either by copying or moving
1052 to a new track, we want to replace the old selection with the new ones
1055 if (new_views.size() > 0) {
1056 _editor->selection->set (new_views);
1059 /* write commands for the accumulated diffs for all our modified playlists */
1060 add_stateful_diff_commands_for_playlists (modified_playlists);
1062 _editor->commit_reversible_command ();
1066 RegionMoveDrag::finished_no_copy (
1067 bool const changed_position,
1068 bool const changed_tracks,
1069 framecnt_t const drag_delta
1072 RegionSelection new_views;
1073 PlaylistSet modified_playlists;
1074 PlaylistSet frozen_playlists;
1075 set<RouteTimeAxisView*> views_to_update;
1076 RouteTimeAxisView* new_time_axis_view = 0;
1079 /* all changes were made during motion event handlers */
1080 _editor->commit_reversible_command ();
1084 if (_x_constrained) {
1085 _editor->begin_reversible_command (_("fixed time region drag"));
1087 _editor->begin_reversible_command (Operations::region_drag);
1090 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1092 RegionView* rv = i->view;
1093 RouteTimeAxisView* dest_rtv = 0;
1095 if (rv->region()->locked() || rv->region()->video_locked()) {
1100 if (i->time_axis_view < 0) {
1101 if (!new_time_axis_view) {
1102 new_time_axis_view = create_destination_time_axis (rv->region());
1104 dest_rtv = new_time_axis_view;
1106 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1111 double const dest_layer = i->layer;
1113 views_to_update.insert (dest_rtv);
1117 if (changed_position && !_x_constrained) {
1118 where = rv->region()->position() - drag_delta;
1120 where = rv->region()->position();
1123 if (changed_tracks) {
1125 /* insert into new playlist */
1127 RegionView* new_view = insert_region_into_playlist (
1128 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1131 if (new_view == 0) {
1136 new_views.push_back (new_view);
1138 /* remove from old playlist */
1140 /* the region that used to be in the old playlist is not
1141 moved to the new one - we use a copy of it. as a result,
1142 any existing editor for the region should no longer be
1145 rv->hide_region_editor();
1146 rv->fake_set_opaque (false);
1148 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1152 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1154 /* this movement may result in a crossfade being modified, or a layering change,
1155 so we need to get undo data from the playlist as well as the region.
1158 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1160 playlist->clear_changes ();
1163 rv->region()->clear_changes ();
1166 motion on the same track. plonk the previously reparented region
1167 back to its original canvas group (its streamview).
1168 No need to do anything for copies as they are fake regions which will be deleted.
1171 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1172 rv->get_canvas_group()->set_y_position (i->initial_y);
1175 /* just change the model */
1176 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1177 playlist->set_layer (rv->region(), dest_layer);
1180 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1182 r = frozen_playlists.insert (playlist);
1185 playlist->freeze ();
1188 rv->region()->set_position (where);
1190 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1193 if (changed_tracks) {
1195 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1196 was selected in all of them, then removing it from a playlist will have removed all
1197 trace of it from _views (i.e. there were N regions selected, we removed 1,
1198 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1199 corresponding regionview, and _views is now empty).
1201 This could have invalidated any and all iterators into _views.
1203 The heuristic we use here is: if the region selection is empty, break out of the loop
1204 here. if the region selection is not empty, then restart the loop because we know that
1205 we must have removed at least the region(view) we've just been working on as well as any
1206 that we processed on previous iterations.
1208 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1209 we can just iterate.
1213 if (_views.empty()) {
1224 /* If we've created new regions either by copying or moving
1225 to a new track, we want to replace the old selection with the new ones
1228 if (new_views.size() > 0) {
1229 _editor->selection->set (new_views);
1232 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1236 /* write commands for the accumulated diffs for all our modified playlists */
1237 add_stateful_diff_commands_for_playlists (modified_playlists);
1239 _editor->commit_reversible_command ();
1241 /* We have futzed with the layering of canvas items on our streamviews.
1242 If any region changed layer, this will have resulted in the stream
1243 views being asked to set up their region views, and all will be well.
1244 If not, we might now have badly-ordered region views. Ask the StreamViews
1245 involved to sort themselves out, just in case.
1248 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1249 (*i)->view()->playlist_layered ((*i)->track ());
1253 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1254 * @param region Region to remove.
1255 * @param playlist playlist To remove from.
1256 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1257 * that clear_changes () is only called once per playlist.
1260 RegionMoveDrag::remove_region_from_playlist (
1261 boost::shared_ptr<Region> region,
1262 boost::shared_ptr<Playlist> playlist,
1263 PlaylistSet& modified_playlists
1266 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1269 playlist->clear_changes ();
1272 playlist->remove_region (region);
1276 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1277 * clearing the playlist's diff history first if necessary.
1278 * @param region Region to insert.
1279 * @param dest_rtv Destination RouteTimeAxisView.
1280 * @param dest_layer Destination layer.
1281 * @param where Destination position.
1282 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1283 * that clear_changes () is only called once per playlist.
1284 * @return New RegionView, or 0 if no insert was performed.
1287 RegionMoveDrag::insert_region_into_playlist (
1288 boost::shared_ptr<Region> region,
1289 RouteTimeAxisView* dest_rtv,
1292 PlaylistSet& modified_playlists
1295 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1296 if (!dest_playlist) {
1300 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1301 _new_region_view = 0;
1302 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1304 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1305 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1307 dest_playlist->clear_changes ();
1310 dest_playlist->add_region (region, where);
1312 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1313 dest_playlist->set_layer (region, dest_layer);
1318 assert (_new_region_view);
1320 return _new_region_view;
1324 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1326 _new_region_view = rv;
1330 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1332 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1333 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1335 _editor->session()->add_command (c);
1344 RegionMoveDrag::aborted (bool movement_occurred)
1348 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1355 RegionMotionDrag::aborted (movement_occurred);
1360 RegionMotionDrag::aborted (bool)
1362 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1364 StreamView* sview = (*i)->view();
1367 if (sview->layer_display() == Expanded) {
1368 sview->set_layer_display (Stacked);
1373 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1374 RegionView* rv = i->view;
1375 TimeAxisView* tv = &(rv->get_time_axis_view ());
1376 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1378 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1379 rv->get_canvas_group()->set_y_position (0);
1381 rv->fake_set_opaque (false);
1382 rv->move (-_total_x_delta, 0);
1383 rv->set_height (rtv->view()->child_height ());
1387 /** @param b true to brush, otherwise false.
1388 * @param c true to make copies of the regions being moved, otherwise false.
1390 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1391 : RegionMotionDrag (e, i, p, v, b),
1394 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1397 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1398 if (rtv && rtv->is_track()) {
1399 speed = rtv->track()->speed ();
1402 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1406 RegionMoveDrag::setup_pointer_frame_offset ()
1408 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1411 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1412 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1414 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1416 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1417 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1419 _primary = v->view()->create_region_view (r, false, false);
1421 _primary->get_canvas_group()->show ();
1422 _primary->set_position (pos, 0);
1423 _views.push_back (DraggingView (_primary, this));
1425 _last_frame_position = pos;
1427 _item = _primary->get_canvas_group ();
1431 RegionInsertDrag::finished (GdkEvent *, bool)
1433 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1435 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1436 _primary->get_canvas_group()->set_y_position (0);
1438 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1440 _editor->begin_reversible_command (Operations::insert_region);
1441 playlist->clear_changes ();
1442 playlist->add_region (_primary->region (), _last_frame_position);
1443 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1444 _editor->commit_reversible_command ();
1452 RegionInsertDrag::aborted (bool)
1459 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1460 : RegionMoveDrag (e, i, p, v, false, false)
1462 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1465 struct RegionSelectionByPosition {
1466 bool operator() (RegionView*a, RegionView* b) {
1467 return a->region()->position () < b->region()->position();
1472 RegionSpliceDrag::motion (GdkEvent* event, bool)
1474 /* Which trackview is this ? */
1476 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1477 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1479 /* The region motion is only processed if the pointer is over
1483 if (!tv || !tv->is_track()) {
1484 /* To make sure we hide the verbose canvas cursor when the mouse is
1485 not held over and audiotrack.
1487 _editor->verbose_cursor()->hide ();
1493 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1499 RegionSelection copy (_editor->selection->regions);
1501 RegionSelectionByPosition cmp;
1504 framepos_t const pf = adjusted_current_frame (event);
1506 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1508 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1514 boost::shared_ptr<Playlist> playlist;
1516 if ((playlist = atv->playlist()) == 0) {
1520 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1525 if (pf < (*i)->region()->last_frame() + 1) {
1529 if (pf > (*i)->region()->first_frame()) {
1535 playlist->shuffle ((*i)->region(), dir);
1540 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1542 RegionMoveDrag::finished (event, movement_occurred);
1546 RegionSpliceDrag::aborted (bool)
1551 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1553 _view (dynamic_cast<MidiTimeAxisView*> (v))
1555 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1561 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1564 _region = add_midi_region (_view);
1565 _view->playlist()->freeze ();
1568 framepos_t const f = adjusted_current_frame (event);
1569 if (f < grab_frame()) {
1570 _region->set_position (f);
1573 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1574 so that if this region is duplicated, its duplicate starts on
1575 a snap point rather than 1 frame after a snap point. Otherwise things get
1576 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1577 place snapped notes at the start of the region.
1580 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1581 _region->set_length (len < 1 ? 1 : len);
1587 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1589 if (!movement_occurred) {
1590 add_midi_region (_view);
1592 _view->playlist()->thaw ();
1597 RegionCreateDrag::aborted (bool)
1600 _view->playlist()->thaw ();
1606 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1610 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1614 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1616 Gdk::Cursor* cursor;
1617 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1619 float x_fraction = cnote->mouse_x_fraction ();
1621 if (x_fraction > 0.0 && x_fraction < 0.25) {
1622 cursor = _editor->cursors()->left_side_trim;
1624 cursor = _editor->cursors()->right_side_trim;
1627 Drag::start_grab (event, cursor);
1629 region = &cnote->region_view();
1631 double const region_start = region->get_position_pixels();
1632 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1634 if (grab_x() <= middle_point) {
1635 cursor = _editor->cursors()->left_side_trim;
1638 cursor = _editor->cursors()->right_side_trim;
1644 if (event->motion.state & Keyboard::PrimaryModifier) {
1650 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1652 if (ms.size() > 1) {
1653 /* has to be relative, may make no sense otherwise */
1657 /* select this note; if it is already selected, preserve the existing selection,
1658 otherwise make this note the only one selected.
1660 region->note_selected (cnote, cnote->selected ());
1662 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1663 MidiRegionSelection::iterator next;
1666 (*r)->begin_resizing (at_front);
1672 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1674 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1675 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1676 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1678 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1683 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1685 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1686 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1687 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1689 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1694 NoteResizeDrag::aborted (bool)
1696 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1697 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1698 (*r)->abort_resizing ();
1702 AVDraggingView::AVDraggingView (RegionView* v)
1705 initial_position = v->region()->position ();
1708 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1711 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1714 TrackViewList empty;
1716 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1717 std::list<RegionView*> views = rs.by_layer();
1719 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1720 RegionView* rv = (*i);
1721 if (!rv->region()->video_locked()) {
1724 _views.push_back (AVDraggingView (rv));
1729 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1731 Drag::start_grab (event);
1732 if (_editor->session() == 0) {
1736 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1737 _max_backwards_drag = (
1738 ARDOUR_UI::instance()->video_timeline->get_duration()
1739 + ARDOUR_UI::instance()->video_timeline->get_offset()
1740 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1743 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1744 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1745 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1748 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1751 Timecode::Time timecode;
1752 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1753 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);
1754 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1755 _editor->verbose_cursor()->show ();
1759 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1761 if (_editor->session() == 0) {
1764 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1768 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1769 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
1771 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1772 dt = - _max_backwards_drag;
1775 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1776 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1778 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1779 RegionView* rv = i->view;
1780 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1783 rv->fake_set_opaque (true);
1784 rv->region()->clear_changes ();
1785 rv->region()->suspend_property_changes();
1787 rv->region()->set_position(i->initial_position + dt);
1788 rv->region_changed(ARDOUR::Properties::position);
1791 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1792 Timecode::Time timecode;
1793 Timecode::Time timediff;
1795 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1796 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1797 snprintf (buf, sizeof (buf),
1798 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1799 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1800 , _("Video Start:"),
1801 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1803 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1805 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1806 _editor->verbose_cursor()->show ();
1810 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1812 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1816 if (!movement_occurred || ! _editor->session()) {
1820 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1822 _editor->begin_reversible_command (_("Move Video"));
1824 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1825 ARDOUR_UI::instance()->video_timeline->save_undo();
1826 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1827 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1829 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1830 i->view->drag_end();
1831 i->view->fake_set_opaque (false);
1832 i->view->region()->resume_property_changes ();
1834 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1837 _editor->session()->maybe_update_session_range(
1838 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1839 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1843 _editor->commit_reversible_command ();
1847 VideoTimeLineDrag::aborted (bool)
1849 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1852 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1853 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1855 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1856 i->view->region()->resume_property_changes ();
1857 i->view->region()->set_position(i->initial_position);
1861 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1862 : RegionDrag (e, i, p, v)
1863 , _preserve_fade_anchor (preserve_fade_anchor)
1864 , _jump_position_when_done (false)
1866 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1870 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1873 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1874 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1876 if (tv && tv->is_track()) {
1877 speed = tv->track()->speed();
1880 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1881 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1882 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1884 framepos_t const pf = adjusted_current_frame (event);
1886 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1887 /* Move the contents of the region around without changing the region bounds */
1888 _operation = ContentsTrim;
1889 Drag::start_grab (event, _editor->cursors()->trimmer);
1891 /* These will get overridden for a point trim.*/
1892 if (pf < (region_start + region_length/2)) {
1893 /* closer to front */
1894 _operation = StartTrim;
1895 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1898 _operation = EndTrim;
1899 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1903 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1904 _jump_position_when_done = true;
1907 switch (_operation) {
1909 show_verbose_cursor_time (region_start);
1910 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1911 i->view->trim_front_starting ();
1915 show_verbose_cursor_time (region_end);
1918 show_verbose_cursor_time (pf);
1922 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1923 i->view->region()->suspend_property_changes ();
1928 TrimDrag::motion (GdkEvent* event, bool first_move)
1930 RegionView* rv = _primary;
1933 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1934 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1935 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1936 frameoffset_t frame_delta = 0;
1938 if (tv && tv->is_track()) {
1939 speed = tv->track()->speed();
1942 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1948 switch (_operation) {
1950 trim_type = "Region start trim";
1953 trim_type = "Region end trim";
1956 trim_type = "Region content trim";
1963 _editor->begin_reversible_command (trim_type);
1965 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1966 RegionView* rv = i->view;
1967 rv->fake_set_opaque (false);
1968 rv->enable_display (false);
1969 rv->region()->playlist()->clear_owned_changes ();
1971 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1974 arv->temporarily_hide_envelope ();
1978 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1979 insert_result = _editor->motion_frozen_playlists.insert (pl);
1981 if (insert_result.second) {
1987 bool non_overlap_trim = false;
1989 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1990 non_overlap_trim = true;
1993 /* contstrain trim to fade length */
1994 if (_preserve_fade_anchor) {
1995 switch (_operation) {
1997 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1998 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2000 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2001 if (ar->locked()) continue;
2002 framecnt_t len = ar->fade_in()->back()->when;
2003 if (len < dt) dt = min(dt, len);
2007 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2008 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2010 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2011 if (ar->locked()) continue;
2012 framecnt_t len = ar->fade_out()->back()->when;
2013 if (len < -dt) dt = max(dt, -len);
2022 switch (_operation) {
2024 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2025 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2026 if (changed && _preserve_fade_anchor) {
2027 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2029 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2030 framecnt_t len = ar->fade_in()->back()->when;
2031 framecnt_t diff = ar->first_frame() - i->initial_position;
2032 framepos_t new_length = len - diff;
2033 i->anchored_fade_length = min (ar->length(), new_length);
2034 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2035 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2042 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2043 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2044 if (changed && _preserve_fade_anchor) {
2045 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2047 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2048 framecnt_t len = ar->fade_out()->back()->when;
2049 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2050 framepos_t new_length = len + diff;
2051 i->anchored_fade_length = min (ar->length(), new_length);
2052 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2053 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2061 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2063 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2064 i->view->move_contents (frame_delta);
2070 switch (_operation) {
2072 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2075 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2078 // show_verbose_cursor_time (frame_delta);
2085 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2087 if (movement_occurred) {
2088 motion (event, false);
2090 if (_operation == StartTrim) {
2091 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2093 /* This must happen before the region's StatefulDiffCommand is created, as it may
2094 `correct' (ahem) the region's _start from being negative to being zero. It
2095 needs to be zero in the undo record.
2097 i->view->trim_front_ending ();
2099 if (_preserve_fade_anchor) {
2100 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2102 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2103 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2104 ar->set_fade_in_length(i->anchored_fade_length);
2105 ar->set_fade_in_active(true);
2108 if (_jump_position_when_done) {
2109 i->view->region()->set_position (i->initial_position);
2112 } else if (_operation == EndTrim) {
2113 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2114 if (_preserve_fade_anchor) {
2115 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2117 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2118 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2119 ar->set_fade_out_length(i->anchored_fade_length);
2120 ar->set_fade_out_active(true);
2123 if (_jump_position_when_done) {
2124 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2129 if (!_views.empty()) {
2130 if (_operation == StartTrim) {
2131 _editor->maybe_locate_with_edit_preroll(
2132 _views.begin()->view->region()->position());
2134 if (_operation == EndTrim) {
2135 _editor->maybe_locate_with_edit_preroll(
2136 _views.begin()->view->region()->position() +
2137 _views.begin()->view->region()->length());
2141 if (!_editor->selection->selected (_primary)) {
2142 _primary->thaw_after_trim ();
2145 set<boost::shared_ptr<Playlist> > diffed_playlists;
2147 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2148 i->view->thaw_after_trim ();
2149 i->view->enable_display (true);
2150 i->view->fake_set_opaque (true);
2152 /* Trimming one region may affect others on the playlist, so we need
2153 to get undo Commands from the whole playlist rather than just the
2154 region. Use diffed_playlists to make sure we don't diff a given
2155 playlist more than once.
2157 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2158 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2159 vector<Command*> cmds;
2161 _editor->session()->add_commands (cmds);
2162 diffed_playlists.insert (p);
2167 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2171 _editor->motion_frozen_playlists.clear ();
2172 _editor->commit_reversible_command();
2175 /* no mouse movement */
2176 _editor->point_trim (event, adjusted_current_frame (event));
2179 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2180 if (_operation == StartTrim) {
2181 i->view->trim_front_ending ();
2184 i->view->region()->resume_property_changes ();
2189 TrimDrag::aborted (bool movement_occurred)
2191 /* Our motion method is changing model state, so use the Undo system
2192 to cancel. Perhaps not ideal, as this will leave an Undo point
2193 behind which may be slightly odd from the user's point of view.
2198 if (movement_occurred) {
2202 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2203 i->view->region()->resume_property_changes ();
2208 TrimDrag::setup_pointer_frame_offset ()
2210 list<DraggingView>::iterator i = _views.begin ();
2211 while (i != _views.end() && i->view != _primary) {
2215 if (i == _views.end()) {
2219 switch (_operation) {
2221 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2224 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2231 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2235 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2236 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2241 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2243 Drag::start_grab (event, cursor);
2244 show_verbose_cursor_time (adjusted_current_frame(event));
2248 MeterMarkerDrag::setup_pointer_frame_offset ()
2250 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2254 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2256 if (!_marker->meter().movable()) {
2262 // create a dummy marker for visual representation of moving the
2263 // section, because whether its a copy or not, we're going to
2264 // leave or lose the original marker (leave if its a copy; lose if its
2265 // not, because we'll remove it from the map).
2267 MeterSection section (_marker->meter());
2269 if (!section.movable()) {
2274 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2276 _marker = new MeterMarker (
2278 *_editor->meter_group,
2279 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2281 *new MeterSection (_marker->meter())
2284 /* use the new marker for the grab */
2285 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2288 TempoMap& map (_editor->session()->tempo_map());
2289 /* get current state */
2290 before_state = &map.get_state();
2291 /* remove the section while we drag it */
2292 map.remove_meter (section, true);
2296 framepos_t const pf = adjusted_current_frame (event);
2297 _marker->set_position (pf);
2298 show_verbose_cursor_time (pf);
2302 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2304 if (!movement_occurred) {
2305 if (was_double_click()) {
2306 _editor->edit_meter_marker (*_marker);
2311 if (!_marker->meter().movable()) {
2315 motion (event, false);
2317 Timecode::BBT_Time when;
2319 TempoMap& map (_editor->session()->tempo_map());
2320 map.bbt_time (last_pointer_frame(), when);
2322 if (_copy == true) {
2323 _editor->begin_reversible_command (_("copy meter mark"));
2324 XMLNode &before = map.get_state();
2325 map.add_meter (_marker->meter(), when);
2326 XMLNode &after = map.get_state();
2327 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2328 _editor->commit_reversible_command ();
2331 _editor->begin_reversible_command (_("move meter mark"));
2333 /* we removed it before, so add it back now */
2335 map.add_meter (_marker->meter(), when);
2336 XMLNode &after = map.get_state();
2337 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2338 _editor->commit_reversible_command ();
2341 // delete the dummy marker we used for visual representation while moving.
2342 // a new visual marker will show up automatically.
2347 MeterMarkerDrag::aborted (bool moved)
2349 _marker->set_position (_marker->meter().frame ());
2352 TempoMap& map (_editor->session()->tempo_map());
2353 /* we removed it before, so add it back now */
2354 map.add_meter (_marker->meter(), _marker->meter().frame());
2355 // delete the dummy marker we used for visual representation while moving.
2356 // a new visual marker will show up automatically.
2361 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2365 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2367 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2372 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2374 Drag::start_grab (event, cursor);
2375 show_verbose_cursor_time (adjusted_current_frame (event));
2379 TempoMarkerDrag::setup_pointer_frame_offset ()
2381 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2385 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2387 if (!_marker->tempo().movable()) {
2393 // create a dummy marker for visual representation of moving the
2394 // section, because whether its a copy or not, we're going to
2395 // leave or lose the original marker (leave if its a copy; lose if its
2396 // not, because we'll remove it from the map).
2398 // create a dummy marker for visual representation of moving the copy.
2399 // The actual copying is not done before we reach the finish callback.
2402 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2404 TempoSection section (_marker->tempo());
2406 _marker = new TempoMarker (
2408 *_editor->tempo_group,
2409 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2411 *new TempoSection (_marker->tempo())
2414 /* use the new marker for the grab */
2415 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2418 TempoMap& map (_editor->session()->tempo_map());
2419 /* get current state */
2420 before_state = &map.get_state();
2421 /* remove the section while we drag it */
2422 map.remove_tempo (section, true);
2426 framepos_t const pf = adjusted_current_frame (event);
2427 _marker->set_position (pf);
2428 show_verbose_cursor_time (pf);
2432 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2434 if (!movement_occurred) {
2435 if (was_double_click()) {
2436 _editor->edit_tempo_marker (*_marker);
2441 if (!_marker->tempo().movable()) {
2445 motion (event, false);
2447 TempoMap& map (_editor->session()->tempo_map());
2448 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2449 Timecode::BBT_Time when;
2451 map.bbt_time (beat_time, when);
2453 if (_copy == true) {
2454 _editor->begin_reversible_command (_("copy tempo mark"));
2455 XMLNode &before = map.get_state();
2456 map.add_tempo (_marker->tempo(), when);
2457 XMLNode &after = map.get_state();
2458 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2459 _editor->commit_reversible_command ();
2462 _editor->begin_reversible_command (_("move tempo mark"));
2463 /* we removed it before, so add it back now */
2464 map.add_tempo (_marker->tempo(), when);
2465 XMLNode &after = map.get_state();
2466 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2467 _editor->commit_reversible_command ();
2470 // delete the dummy marker we used for visual representation while moving.
2471 // a new visual marker will show up automatically.
2476 TempoMarkerDrag::aborted (bool moved)
2478 _marker->set_position (_marker->tempo().frame());
2480 TempoMap& map (_editor->session()->tempo_map());
2481 /* we removed it before, so add it back now */
2482 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2483 // delete the dummy marker we used for visual representation while moving.
2484 // a new visual marker will show up automatically.
2489 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2490 : Drag (e, &c.track_canvas_item())
2494 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2497 /** Do all the things we do when dragging the playhead to make it look as though
2498 * we have located, without actually doing the locate (because that would cause
2499 * the diskstream buffers to be refilled, which is too slow).
2502 CursorDrag::fake_locate (framepos_t t)
2504 _editor->playhead_cursor->set_position (t);
2506 Session* s = _editor->session ();
2507 if (s->timecode_transmission_suspended ()) {
2508 framepos_t const f = _editor->playhead_cursor->current_frame ();
2509 /* This is asynchronous so it will be sent "now"
2511 s->send_mmc_locate (f);
2512 /* These are synchronous and will be sent during the next
2515 s->queue_full_time_code ();
2516 s->queue_song_position_pointer ();
2519 show_verbose_cursor_time (t);
2520 _editor->UpdateAllTransportClocks (t);
2524 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2526 Drag::start_grab (event, c);
2528 _grab_zoom = _editor->samples_per_pixel;
2530 framepos_t where = _editor->canvas_event_sample (event);
2532 _editor->snap_to_with_modifier (where, event);
2534 _editor->_dragging_playhead = true;
2536 Session* s = _editor->session ();
2538 /* grab the track canvas item as well */
2540 _cursor.track_canvas_item().grab();
2543 if (_was_rolling && _stop) {
2547 if (s->is_auditioning()) {
2548 s->cancel_audition ();
2552 if (AudioEngine::instance()->connected()) {
2554 /* do this only if we're the engine is connected
2555 * because otherwise this request will never be
2556 * serviced and we'll busy wait forever. likewise,
2557 * notice if we are disconnected while waiting for the
2558 * request to be serviced.
2561 s->request_suspend_timecode_transmission ();
2562 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2563 /* twiddle our thumbs */
2568 fake_locate (where);
2572 CursorDrag::motion (GdkEvent* event, bool)
2574 framepos_t const adjusted_frame = adjusted_current_frame (event);
2575 if (adjusted_frame != last_pointer_frame()) {
2576 fake_locate (adjusted_frame);
2581 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2583 _editor->_dragging_playhead = false;
2585 _cursor.track_canvas_item().ungrab();
2587 if (!movement_occurred && _stop) {
2591 motion (event, false);
2593 Session* s = _editor->session ();
2595 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2596 _editor->_pending_locate_request = true;
2597 s->request_resume_timecode_transmission ();
2602 CursorDrag::aborted (bool)
2604 _cursor.track_canvas_item().ungrab();
2606 if (_editor->_dragging_playhead) {
2607 _editor->session()->request_resume_timecode_transmission ();
2608 _editor->_dragging_playhead = false;
2611 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2614 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2615 : RegionDrag (e, i, p, v)
2617 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2621 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2623 Drag::start_grab (event, cursor);
2625 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2626 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2628 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2632 FadeInDrag::setup_pointer_frame_offset ()
2634 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2635 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2636 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2640 FadeInDrag::motion (GdkEvent* event, bool)
2642 framecnt_t fade_length;
2644 framepos_t const pos = adjusted_current_frame (event);
2646 boost::shared_ptr<Region> region = _primary->region ();
2648 if (pos < (region->position() + 64)) {
2649 fade_length = 64; // this should be a minimum defined somewhere
2650 } else if (pos > region->last_frame()) {
2651 fade_length = region->length();
2653 fade_length = pos - region->position();
2656 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2658 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2664 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2667 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2671 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2673 if (!movement_occurred) {
2677 framecnt_t fade_length;
2679 framepos_t const pos = adjusted_current_frame (event);
2681 boost::shared_ptr<Region> region = _primary->region ();
2683 if (pos < (region->position() + 64)) {
2684 fade_length = 64; // this should be a minimum defined somewhere
2685 } else if (pos > region->last_frame()) {
2686 fade_length = region->length();
2688 fade_length = pos - region->position();
2691 _editor->begin_reversible_command (_("change fade in length"));
2693 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2695 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2701 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2702 XMLNode &before = alist->get_state();
2704 tmp->audio_region()->set_fade_in_length (fade_length);
2705 tmp->audio_region()->set_fade_in_active (true);
2707 XMLNode &after = alist->get_state();
2708 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2711 _editor->commit_reversible_command ();
2715 FadeInDrag::aborted (bool)
2717 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2718 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2724 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2728 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2729 : RegionDrag (e, i, p, v)
2731 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2735 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2737 Drag::start_grab (event, cursor);
2739 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2740 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2742 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2746 FadeOutDrag::setup_pointer_frame_offset ()
2748 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2749 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2750 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2754 FadeOutDrag::motion (GdkEvent* event, bool)
2756 framecnt_t fade_length;
2758 framepos_t const pos = adjusted_current_frame (event);
2760 boost::shared_ptr<Region> region = _primary->region ();
2762 if (pos > (region->last_frame() - 64)) {
2763 fade_length = 64; // this should really be a minimum fade defined somewhere
2765 else if (pos < region->position()) {
2766 fade_length = region->length();
2769 fade_length = region->last_frame() - pos;
2772 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2774 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2780 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2783 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2787 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2789 if (!movement_occurred) {
2793 framecnt_t fade_length;
2795 framepos_t const pos = adjusted_current_frame (event);
2797 boost::shared_ptr<Region> region = _primary->region ();
2799 if (pos > (region->last_frame() - 64)) {
2800 fade_length = 64; // this should really be a minimum fade defined somewhere
2802 else if (pos < region->position()) {
2803 fade_length = region->length();
2806 fade_length = region->last_frame() - pos;
2809 _editor->begin_reversible_command (_("change fade out length"));
2811 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2813 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2819 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2820 XMLNode &before = alist->get_state();
2822 tmp->audio_region()->set_fade_out_length (fade_length);
2823 tmp->audio_region()->set_fade_out_active (true);
2825 XMLNode &after = alist->get_state();
2826 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2829 _editor->commit_reversible_command ();
2833 FadeOutDrag::aborted (bool)
2835 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2836 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2842 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2846 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2849 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2851 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2854 _points.push_back (ArdourCanvas::Duple (0, 0));
2855 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2858 MarkerDrag::~MarkerDrag ()
2860 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2865 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2867 location = new Location (*l);
2868 markers.push_back (m);
2873 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2875 Drag::start_grab (event, cursor);
2879 Location *location = _editor->find_location_from_marker (_marker, is_start);
2880 _editor->_dragging_edit_point = true;
2882 update_item (location);
2884 // _drag_line->show();
2885 // _line->raise_to_top();
2888 show_verbose_cursor_time (location->start());
2890 show_verbose_cursor_time (location->end());
2893 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2896 case Selection::Toggle:
2897 /* we toggle on the button release */
2899 case Selection::Set:
2900 if (!_editor->selection->selected (_marker)) {
2901 _editor->selection->set (_marker);
2904 case Selection::Extend:
2906 Locations::LocationList ll;
2907 list<Marker*> to_add;
2909 _editor->selection->markers.range (s, e);
2910 s = min (_marker->position(), s);
2911 e = max (_marker->position(), e);
2914 if (e < max_framepos) {
2917 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2918 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2919 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2922 to_add.push_back (lm->start);
2925 to_add.push_back (lm->end);
2929 if (!to_add.empty()) {
2930 _editor->selection->add (to_add);
2934 case Selection::Add:
2935 _editor->selection->add (_marker);
2939 /* Set up copies for us to manipulate during the drag
2942 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2944 Location* l = _editor->find_location_from_marker (*i, is_start);
2951 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2953 /* range: check that the other end of the range isn't
2956 CopiedLocationInfo::iterator x;
2957 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2958 if (*(*x).location == *l) {
2962 if (x == _copied_locations.end()) {
2963 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2965 (*x).markers.push_back (*i);
2966 (*x).move_both = true;
2974 MarkerDrag::setup_pointer_frame_offset ()
2977 Location *location = _editor->find_location_from_marker (_marker, is_start);
2978 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2982 MarkerDrag::motion (GdkEvent* event, bool)
2984 framecnt_t f_delta = 0;
2986 bool move_both = false;
2987 Location *real_location;
2988 Location *copy_location = 0;
2990 framepos_t const newframe = adjusted_current_frame (event);
2991 framepos_t next = newframe;
2993 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2997 CopiedLocationInfo::iterator x;
2999 /* find the marker we're dragging, and compute the delta */
3001 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3003 copy_location = (*x).location;
3005 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3007 /* this marker is represented by this
3008 * CopiedLocationMarkerInfo
3011 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3016 if (real_location->is_mark()) {
3017 f_delta = newframe - copy_location->start();
3021 switch (_marker->type()) {
3022 case Marker::SessionStart:
3023 case Marker::RangeStart:
3024 case Marker::LoopStart:
3025 case Marker::PunchIn:
3026 f_delta = newframe - copy_location->start();
3029 case Marker::SessionEnd:
3030 case Marker::RangeEnd:
3031 case Marker::LoopEnd:
3032 case Marker::PunchOut:
3033 f_delta = newframe - copy_location->end();
3036 /* what kind of marker is this ? */
3045 if (x == _copied_locations.end()) {
3046 /* hmm, impossible - we didn't find the dragged marker */
3050 /* now move them all */
3052 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3054 copy_location = x->location;
3056 /* call this to find out if its the start or end */
3058 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3062 if (real_location->locked()) {
3066 if (copy_location->is_mark()) {
3070 copy_location->set_start (copy_location->start() + f_delta);
3074 framepos_t new_start = copy_location->start() + f_delta;
3075 framepos_t new_end = copy_location->end() + f_delta;
3077 if (is_start) { // start-of-range marker
3079 if (move_both || (*x).move_both) {
3080 copy_location->set_start (new_start);
3081 copy_location->set_end (new_end);
3082 } else if (new_start < copy_location->end()) {
3083 copy_location->set_start (new_start);
3084 } else if (newframe > 0) {
3085 _editor->snap_to (next, 1, true);
3086 copy_location->set_end (next);
3087 copy_location->set_start (newframe);
3090 } else { // end marker
3092 if (move_both || (*x).move_both) {
3093 copy_location->set_end (new_end);
3094 copy_location->set_start (new_start);
3095 } else if (new_end > copy_location->start()) {
3096 copy_location->set_end (new_end);
3097 } else if (newframe > 0) {
3098 _editor->snap_to (next, -1, true);
3099 copy_location->set_start (next);
3100 copy_location->set_end (newframe);
3105 update_item (copy_location);
3107 /* now lookup the actual GUI items used to display this
3108 * location and move them to wherever the copy of the location
3109 * is now. This means that the logic in ARDOUR::Location is
3110 * still enforced, even though we are not (yet) modifying
3111 * the real Location itself.
3114 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3117 lm->set_position (copy_location->start(), copy_location->end());
3122 assert (!_copied_locations.empty());
3124 show_verbose_cursor_time (newframe);
3128 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3130 if (!movement_occurred) {
3132 if (was_double_click()) {
3133 _editor->rename_marker (_marker);
3137 /* just a click, do nothing but finish
3138 off the selection process
3141 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3144 case Selection::Set:
3145 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3146 _editor->selection->set (_marker);
3150 case Selection::Toggle:
3151 /* we toggle on the button release, click only */
3152 _editor->selection->toggle (_marker);
3155 case Selection::Extend:
3156 case Selection::Add:
3163 _editor->_dragging_edit_point = false;
3165 _editor->begin_reversible_command ( _("move marker") );
3166 XMLNode &before = _editor->session()->locations()->get_state();
3168 MarkerSelection::iterator i;
3169 CopiedLocationInfo::iterator x;
3172 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3173 x != _copied_locations.end() && i != _editor->selection->markers.end();
3176 Location * location = _editor->find_location_from_marker (*i, is_start);
3180 if (location->locked()) {
3184 if (location->is_mark()) {
3185 location->set_start (((*x).location)->start());
3187 location->set (((*x).location)->start(), ((*x).location)->end());
3192 XMLNode &after = _editor->session()->locations()->get_state();
3193 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3194 _editor->commit_reversible_command ();
3198 MarkerDrag::aborted (bool)
3204 MarkerDrag::update_item (Location*)
3209 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3211 _cumulative_x_drag (0),
3212 _cumulative_y_drag (0)
3214 if (_zero_gain_fraction < 0.0) {
3215 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3218 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3220 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3226 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3228 Drag::start_grab (event, _editor->cursors()->fader);
3230 // start the grab at the center of the control point so
3231 // the point doesn't 'jump' to the mouse after the first drag
3232 _fixed_grab_x = _point->get_x();
3233 _fixed_grab_y = _point->get_y();
3235 float const fraction = 1 - (_point->get_y() / _point->line().height());
3237 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3239 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3240 event->button.x + 10, event->button.y + 10);
3242 _editor->verbose_cursor()->show ();
3244 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3246 if (!_point->can_slide ()) {
3247 _x_constrained = true;
3252 ControlPointDrag::motion (GdkEvent* event, bool)
3254 double dx = _drags->current_pointer_x() - last_pointer_x();
3255 double dy = _drags->current_pointer_y() - last_pointer_y();
3257 if (event->button.state & Keyboard::SecondaryModifier) {
3262 /* coordinate in pixels relative to the start of the region (for region-based automation)
3263 or track (for track-based automation) */
3264 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3265 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3267 // calculate zero crossing point. back off by .01 to stay on the
3268 // positive side of zero
3269 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3271 // make sure we hit zero when passing through
3272 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3276 if (_x_constrained) {
3279 if (_y_constrained) {
3283 _cumulative_x_drag = cx - _fixed_grab_x;
3284 _cumulative_y_drag = cy - _fixed_grab_y;
3288 cy = min ((double) _point->line().height(), cy);
3290 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3292 if (!_x_constrained) {
3293 _editor->snap_to_with_modifier (cx_frames, event);
3296 cx_frames = min (cx_frames, _point->line().maximum_time());
3298 float const fraction = 1.0 - (cy / _point->line().height());
3300 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3302 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3306 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3308 if (!movement_occurred) {
3312 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3313 _editor->reset_point_selection ();
3317 motion (event, false);
3320 _point->line().end_drag (_pushing, _final_index);
3321 _editor->session()->commit_reversible_command ();
3325 ControlPointDrag::aborted (bool)
3327 _point->line().reset ();
3331 ControlPointDrag::active (Editing::MouseMode m)
3333 if (m == Editing::MouseGain) {
3334 /* always active in mouse gain */
3338 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3339 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3342 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3345 _cumulative_y_drag (0)
3347 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3351 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3353 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3356 _item = &_line->grab_item ();
3358 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3359 origin, and ditto for y.
3362 double cx = event->button.x;
3363 double cy = event->button.y;
3365 _line->parent_group().canvas_to_item (cx, cy);
3367 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3372 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3373 /* no adjacent points */
3377 Drag::start_grab (event, _editor->cursors()->fader);
3379 /* store grab start in parent frame */
3384 double fraction = 1.0 - (cy / _line->height());
3386 _line->start_drag_line (before, after, fraction);
3388 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3389 event->button.x + 10, event->button.y + 10);
3391 _editor->verbose_cursor()->show ();
3395 LineDrag::motion (GdkEvent* event, bool)
3397 double dy = _drags->current_pointer_y() - last_pointer_y();
3399 if (event->button.state & Keyboard::SecondaryModifier) {
3403 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3405 _cumulative_y_drag = cy - _fixed_grab_y;
3408 cy = min ((double) _line->height(), cy);
3410 double const fraction = 1.0 - (cy / _line->height());
3413 /* we are ignoring x position for this drag, so we can just pass in anything */
3414 _line->drag_motion (0, fraction, true, false, ignored);
3416 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3420 LineDrag::finished (GdkEvent* event, bool movement_occured)
3422 if (movement_occured) {
3423 motion (event, false);
3424 _line->end_drag (false, 0);
3426 /* add a new control point on the line */
3428 AutomationTimeAxisView* atv;
3430 _line->end_drag (false, 0);
3432 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3433 framepos_t where = _editor->window_event_sample (event, 0, 0);
3434 atv->add_automation_event (event, where, event->button.y, false);
3438 _editor->session()->commit_reversible_command ();
3442 LineDrag::aborted (bool)
3447 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3450 _cumulative_x_drag (0)
3452 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3456 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3458 Drag::start_grab (event);
3460 _line = reinterpret_cast<Line*> (_item);
3463 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3465 double cx = event->button.x;
3466 double cy = event->button.y;
3468 _item->parent()->canvas_to_item (cx, cy);
3470 /* store grab start in parent frame */
3471 _region_view_grab_x = cx;
3473 _before = *(float*) _item->get_data ("position");
3475 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3477 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3481 FeatureLineDrag::motion (GdkEvent*, bool)
3483 double dx = _drags->current_pointer_x() - last_pointer_x();
3485 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3487 _cumulative_x_drag += dx;
3489 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3498 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3500 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3502 float *pos = new float;
3505 _line->set_data ("position", pos);
3511 FeatureLineDrag::finished (GdkEvent*, bool)
3513 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3514 _arv->update_transient(_before, _before);
3518 FeatureLineDrag::aborted (bool)
3523 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3525 , _vertical_only (false)
3527 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3531 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3533 Drag::start_grab (event);
3534 show_verbose_cursor_time (adjusted_current_frame (event));
3538 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3545 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3547 framepos_t grab = grab_frame ();
3548 if (Config->get_rubberbanding_snaps_to_grid ()) {
3549 _editor->snap_to_with_modifier (grab, event);
3552 /* base start and end on initial click position */
3562 if (_drags->current_pointer_y() < grab_y()) {
3563 y1 = _drags->current_pointer_y();
3566 y2 = _drags->current_pointer_y();
3571 if (start != end || y1 != y2) {
3573 double x1 = _editor->sample_to_pixel (start);
3574 double x2 = _editor->sample_to_pixel (end);
3575 const double min_dimension = 2.0;
3577 _editor->rubberband_rect->set_x0 (x1);
3578 if (_vertical_only) {
3579 /* fixed 10 pixel width */
3580 _editor->rubberband_rect->set_x1 (x1 + 10);
3583 x2 = min (x1 - min_dimension, x2);
3585 x2 = max (x1 + min_dimension, x2);
3587 _editor->rubberband_rect->set_x1 (x2);
3590 _editor->rubberband_rect->set_y0 (y1);
3592 y2 = min (y1 - min_dimension, y2);
3594 y2 = max (y1 + min_dimension, y2);
3597 _editor->rubberband_rect->set_y1 (y2);
3599 _editor->rubberband_rect->show();
3600 _editor->rubberband_rect->raise_to_top();
3602 show_verbose_cursor_time (pf);
3604 do_select_things (event, true);
3609 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3614 if (grab_frame() < last_pointer_frame()) {
3616 x2 = last_pointer_frame ();
3619 x1 = last_pointer_frame ();
3625 if (_drags->current_pointer_y() < grab_y()) {
3626 y1 = _drags->current_pointer_y();
3629 y2 = _drags->current_pointer_y();
3633 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3637 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3639 if (movement_occurred) {
3641 motion (event, false);
3642 do_select_things (event, false);
3648 bool do_deselect = true;
3649 MidiTimeAxisView* mtv;
3651 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3653 if (_editor->selection->empty()) {
3654 /* nothing selected */
3655 add_midi_region (mtv);
3656 do_deselect = false;
3660 /* do not deselect if Primary or Tertiary (toggle-select or
3661 * extend-select are pressed.
3664 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3665 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3672 _editor->rubberband_rect->hide();
3676 RubberbandSelectDrag::aborted (bool)
3678 _editor->rubberband_rect->hide ();
3681 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3682 : RegionDrag (e, i, p, v)
3684 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3688 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3690 Drag::start_grab (event, cursor);
3692 show_verbose_cursor_time (adjusted_current_frame (event));
3696 TimeFXDrag::motion (GdkEvent* event, bool)
3698 RegionView* rv = _primary;
3699 StreamView* cv = rv->get_time_axis_view().view ();
3701 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3702 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3703 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3705 framepos_t const pf = adjusted_current_frame (event);
3707 if (pf > rv->region()->position()) {
3708 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3711 show_verbose_cursor_time (pf);
3715 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3717 _primary->get_time_axis_view().hide_timestretch ();
3719 if (!movement_occurred) {
3723 if (last_pointer_frame() < _primary->region()->position()) {
3724 /* backwards drag of the left edge - not usable */
3728 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3730 float percentage = (double) newlen / (double) _primary->region()->length();
3732 #ifndef USE_RUBBERBAND
3733 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3734 if (_primary->region()->data_type() == DataType::AUDIO) {
3735 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3739 if (!_editor->get_selection().regions.empty()) {
3740 /* primary will already be included in the selection, and edit
3741 group shared editing will propagate selection across
3742 equivalent regions, so just use the current region
3746 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3747 error << _("An error occurred while executing time stretch operation") << endmsg;
3753 TimeFXDrag::aborted (bool)
3755 _primary->get_time_axis_view().hide_timestretch ();
3758 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3761 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3765 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3767 Drag::start_grab (event);
3771 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3773 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3777 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3779 if (movement_occurred && _editor->session()) {
3780 /* make sure we stop */
3781 _editor->session()->request_transport_speed (0.0);
3786 ScrubDrag::aborted (bool)
3791 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3796 , _original_pointer_time_axis (-1)
3797 , _last_pointer_time_axis (-1)
3798 , _time_selection_at_start (!_editor->get_selection().time.empty())
3800 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3802 if (_time_selection_at_start) {
3803 start_at_start = _editor->get_selection().time.start();
3804 end_at_start = _editor->get_selection().time.end_frame();
3809 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3811 if (_editor->session() == 0) {
3815 Gdk::Cursor* cursor = 0;
3817 switch (_operation) {
3818 case CreateSelection:
3819 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3824 cursor = _editor->cursors()->selector;
3825 Drag::start_grab (event, cursor);
3828 case SelectionStartTrim:
3829 if (_editor->clicked_axisview) {
3830 _editor->clicked_axisview->order_selection_trims (_item, true);
3832 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3835 case SelectionEndTrim:
3836 if (_editor->clicked_axisview) {
3837 _editor->clicked_axisview->order_selection_trims (_item, false);
3839 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3843 Drag::start_grab (event, cursor);
3846 case SelectionExtend:
3847 Drag::start_grab (event, cursor);
3851 if (_operation == SelectionMove) {
3852 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3854 show_verbose_cursor_time (adjusted_current_frame (event));
3857 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3861 SelectionDrag::setup_pointer_frame_offset ()
3863 switch (_operation) {
3864 case CreateSelection:
3865 _pointer_frame_offset = 0;
3868 case SelectionStartTrim:
3870 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3873 case SelectionEndTrim:
3874 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3877 case SelectionExtend:
3883 SelectionDrag::motion (GdkEvent* event, bool first_move)
3885 framepos_t start = 0;
3887 framecnt_t length = 0;
3888 framecnt_t distance = 0;
3890 framepos_t const pending_position = adjusted_current_frame (event);
3892 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
3896 switch (_operation) {
3897 case CreateSelection:
3899 framepos_t grab = grab_frame ();
3902 grab = adjusted_current_frame (event, false);
3903 if (grab < pending_position) {
3904 _editor->snap_to (grab, -1);
3906 _editor->snap_to (grab, 1);
3910 if (pending_position < grab) {
3911 start = pending_position;
3914 end = pending_position;
3918 /* first drag: Either add to the selection
3919 or create a new selection
3926 /* adding to the selection */
3927 _editor->set_selected_track_as_side_effect (Selection::Add);
3928 _editor->clicked_selection = _editor->selection->add (start, end);
3935 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3936 _editor->set_selected_track_as_side_effect (Selection::Set);
3939 _editor->clicked_selection = _editor->selection->set (start, end);
3943 /* select all tracks within the rectangle that we've marked out so far */
3944 TrackViewList to_be_added_to_selection;
3945 TrackViewList to_be_removed_from_selection;
3946 TrackViewList& all_tracks (_editor->track_views);
3948 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
3950 if ((*i)->covered_by_y_range (grab_y(), _drags->current_pointer_y())) {
3951 if (!(*i)->get_selected()) {
3952 to_be_added_to_selection.push_back (*i);
3955 if ((*i)->get_selected()) {
3956 to_be_removed_from_selection.push_back (*i);
3961 if (!to_be_added_to_selection.empty()) {
3962 _editor->selection->add (to_be_added_to_selection);
3965 if (!to_be_removed_from_selection.empty()) {
3966 _editor->selection->remove (to_be_removed_from_selection);
3971 case SelectionStartTrim:
3973 start = _editor->selection->time[_editor->clicked_selection].start;
3974 end = _editor->selection->time[_editor->clicked_selection].end;
3976 if (pending_position > end) {
3979 start = pending_position;
3983 case SelectionEndTrim:
3985 start = _editor->selection->time[_editor->clicked_selection].start;
3986 end = _editor->selection->time[_editor->clicked_selection].end;
3988 if (pending_position < start) {
3991 end = pending_position;
3998 start = _editor->selection->time[_editor->clicked_selection].start;
3999 end = _editor->selection->time[_editor->clicked_selection].end;
4001 length = end - start;
4002 distance = pending_position - start;
4003 start = pending_position;
4004 _editor->snap_to (start);
4006 end = start + length;
4010 case SelectionExtend:
4015 switch (_operation) {
4017 if (_time_selection_at_start) {
4018 _editor->selection->move_time (distance);
4022 _editor->selection->replace (_editor->clicked_selection, start, end);
4026 if (_operation == SelectionMove) {
4027 show_verbose_cursor_time(start);
4029 show_verbose_cursor_time(pending_position);
4034 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4036 Session* s = _editor->session();
4038 if (movement_occurred) {
4039 motion (event, false);
4040 /* XXX this is not object-oriented programming at all. ick */
4041 if (_editor->selection->time.consolidate()) {
4042 _editor->selection->TimeChanged ();
4045 /* XXX what if its a music time selection? */
4047 if ( s->get_play_range() && s->transport_rolling() ) {
4048 s->request_play_range (&_editor->selection->time, true);
4050 if (Config->get_always_play_range() && !s->transport_rolling()) {
4051 s->request_locate (_editor->get_selection().time.start());
4057 /* just a click, no pointer movement.
4060 if (_operation == SelectionExtend) {
4061 if (_time_selection_at_start) {
4062 framepos_t pos = adjusted_current_frame (event, false);
4063 framepos_t start = min (pos, start_at_start);
4064 framepos_t end = max (pos, end_at_start);
4065 _editor->selection->set (start, end);
4068 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4069 if (_editor->clicked_selection) {
4070 _editor->selection->remove (_editor->clicked_selection);
4073 if (!_editor->clicked_selection) {
4074 _editor->selection->clear_time();
4079 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4080 _editor->selection->set (_editor->clicked_axisview);
4083 if (s && s->get_play_range () && s->transport_rolling()) {
4084 s->request_stop (false, false);
4089 _editor->stop_canvas_autoscroll ();
4090 _editor->clicked_selection = 0;
4094 SelectionDrag::aborted (bool)
4099 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4104 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4106 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4107 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4108 physical_screen_height (_editor->get_window())));
4109 _drag_rect->hide ();
4111 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4112 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4116 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4118 if (_editor->session() == 0) {
4122 Gdk::Cursor* cursor = 0;
4124 if (!_editor->temp_location) {
4125 _editor->temp_location = new Location (*_editor->session());
4128 switch (_operation) {
4129 case CreateRangeMarker:
4130 case CreateTransportMarker:
4131 case CreateCDMarker:
4133 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4138 cursor = _editor->cursors()->selector;
4142 Drag::start_grab (event, cursor);
4144 show_verbose_cursor_time (adjusted_current_frame (event));
4148 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4150 framepos_t start = 0;
4152 ArdourCanvas::Rectangle *crect;
4154 switch (_operation) {
4155 case CreateRangeMarker:
4156 crect = _editor->range_bar_drag_rect;
4158 case CreateTransportMarker:
4159 crect = _editor->transport_bar_drag_rect;
4161 case CreateCDMarker:
4162 crect = _editor->cd_marker_bar_drag_rect;
4165 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4170 framepos_t const pf = adjusted_current_frame (event);
4172 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4173 framepos_t grab = grab_frame ();
4174 _editor->snap_to (grab);
4176 if (pf < grab_frame()) {
4184 /* first drag: Either add to the selection
4185 or create a new selection.
4190 _editor->temp_location->set (start, end);
4194 update_item (_editor->temp_location);
4196 //_drag_rect->raise_to_top();
4202 _editor->temp_location->set (start, end);
4204 double x1 = _editor->sample_to_pixel (start);
4205 double x2 = _editor->sample_to_pixel (end);
4209 update_item (_editor->temp_location);
4212 show_verbose_cursor_time (pf);
4217 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4219 Location * newloc = 0;
4223 if (movement_occurred) {
4224 motion (event, false);
4227 switch (_operation) {
4228 case CreateRangeMarker:
4229 case CreateCDMarker:
4231 _editor->begin_reversible_command (_("new range marker"));
4232 XMLNode &before = _editor->session()->locations()->get_state();
4233 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4234 if (_operation == CreateCDMarker) {
4235 flags = Location::IsRangeMarker | Location::IsCDMarker;
4236 _editor->cd_marker_bar_drag_rect->hide();
4239 flags = Location::IsRangeMarker;
4240 _editor->range_bar_drag_rect->hide();
4242 newloc = new Location (
4243 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4246 _editor->session()->locations()->add (newloc, true);
4247 XMLNode &after = _editor->session()->locations()->get_state();
4248 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4249 _editor->commit_reversible_command ();
4253 case CreateTransportMarker:
4254 // popup menu to pick loop or punch
4255 _editor->new_transport_marker_context_menu (&event->button, _item);
4261 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4263 if (_operation == CreateTransportMarker) {
4265 /* didn't drag, so just locate */
4267 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4269 } else if (_operation == CreateCDMarker) {
4271 /* didn't drag, but mark is already created so do
4274 } else { /* operation == CreateRangeMarker */
4280 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4282 if (end == max_framepos) {
4283 end = _editor->session()->current_end_frame ();
4286 if (start == max_framepos) {
4287 start = _editor->session()->current_start_frame ();
4290 switch (_editor->mouse_mode) {
4292 /* find the two markers on either side and then make the selection from it */
4293 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4297 /* find the two markers on either side of the click and make the range out of it */
4298 _editor->selection->set (start, end);
4307 _editor->stop_canvas_autoscroll ();
4311 RangeMarkerBarDrag::aborted (bool)
4317 RangeMarkerBarDrag::update_item (Location* location)
4319 double const x1 = _editor->sample_to_pixel (location->start());
4320 double const x2 = _editor->sample_to_pixel (location->end());
4322 _drag_rect->set_x0 (x1);
4323 _drag_rect->set_x1 (x2);
4326 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4330 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4334 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4336 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4337 Drag::start_grab (event, _editor->cursors()->zoom_out);
4340 Drag::start_grab (event, _editor->cursors()->zoom_in);
4344 show_verbose_cursor_time (adjusted_current_frame (event));
4348 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4353 framepos_t const pf = adjusted_current_frame (event);
4355 framepos_t grab = grab_frame ();
4356 _editor->snap_to_with_modifier (grab, event);
4358 /* base start and end on initial click position */
4370 _editor->zoom_rect->show();
4371 _editor->zoom_rect->raise_to_top();
4374 _editor->reposition_zoom_rect(start, end);
4376 show_verbose_cursor_time (pf);
4381 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4383 if (movement_occurred) {
4384 motion (event, false);
4386 if (grab_frame() < last_pointer_frame()) {
4387 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4389 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4392 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4393 _editor->tav_zoom_step (_zoom_out);
4395 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4399 _editor->zoom_rect->hide();
4403 MouseZoomDrag::aborted (bool)
4405 _editor->zoom_rect->hide ();
4408 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4410 , _cumulative_dx (0)
4411 , _cumulative_dy (0)
4413 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4415 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4417 _region = &_primary->region_view ();
4418 _note_height = _region->midi_stream_view()->note_height ();
4422 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4424 Drag::start_grab (event);
4426 if (!(_was_selected = _primary->selected())) {
4428 /* tertiary-click means extend selection - we'll do that on button release,
4429 so don't add it here, because otherwise we make it hard to figure
4430 out the "extend-to" range.
4433 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4436 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4439 _region->note_selected (_primary, true);
4441 _region->unique_select (_primary);
4447 /** @return Current total drag x change in frames */
4449 NoteDrag::total_dx () const
4452 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4454 /* primary note time */
4455 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4457 /* new time of the primary note in session frames */
4458 frameoffset_t st = n + dx;
4460 framepos_t const rp = _region->region()->position ();
4462 /* prevent the note being dragged earlier than the region's position */
4465 /* snap and return corresponding delta */
4466 return _region->snap_frame_to_frame (st - rp) + rp - n;
4469 /** @return Current total drag y change in note number */
4471 NoteDrag::total_dy () const
4473 MidiStreamView* msv = _region->midi_stream_view ();
4474 double const y = _region->midi_view()->y_position ();
4475 /* new current note */
4476 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4478 n = max (msv->lowest_note(), n);
4479 n = min (msv->highest_note(), n);
4480 /* and work out delta */
4481 return n - msv->y_to_note (grab_y() - y);
4485 NoteDrag::motion (GdkEvent *, bool)
4487 /* Total change in x and y since the start of the drag */
4488 frameoffset_t const dx = total_dx ();
4489 int8_t const dy = total_dy ();
4491 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4492 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4493 double const tdy = -dy * _note_height - _cumulative_dy;
4496 _cumulative_dx += tdx;
4497 _cumulative_dy += tdy;
4499 int8_t note_delta = total_dy();
4501 _region->move_selection (tdx, tdy, note_delta);
4503 /* the new note value may be the same as the old one, but we
4504 * don't know what that means because the selection may have
4505 * involved more than one note and we might be doing something
4506 * odd with them. so show the note value anyway, always.
4510 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4512 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4513 (int) floor ((double)new_note));
4515 show_verbose_cursor_text (buf);
4520 NoteDrag::finished (GdkEvent* ev, bool moved)
4523 /* no motion - select note */
4525 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4526 _editor->current_mouse_mode() == Editing::MouseDraw) {
4528 if (_was_selected) {
4529 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4531 _region->note_deselected (_primary);
4534 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4535 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4537 if (!extend && !add && _region->selection_size() > 1) {
4538 _region->unique_select (_primary);
4539 } else if (extend) {
4540 _region->note_selected (_primary, true, true);
4542 /* it was added during button press */
4547 _region->note_dropped (_primary, total_dx(), total_dy());
4552 NoteDrag::aborted (bool)
4557 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4558 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4559 : Drag (editor, atv->base_item ())
4561 , _nothing_to_drag (false)
4563 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4564 y_origin = atv->y_position();
4565 setup (atv->lines ());
4568 /** Make an AutomationRangeDrag for region gain lines */
4569 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4570 : Drag (editor, rv->get_canvas_group ())
4572 , _nothing_to_drag (false)
4574 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4576 list<boost::shared_ptr<AutomationLine> > lines;
4577 lines.push_back (rv->get_gain_line ());
4578 y_origin = rv->get_time_axis_view().y_position();
4582 /** @param lines AutomationLines to drag.
4583 * @param offset Offset from the session start to the points in the AutomationLines.
4586 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4588 /* find the lines that overlap the ranges being dragged */
4589 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4590 while (i != lines.end ()) {
4591 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4594 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4596 /* check this range against all the AudioRanges that we are using */
4597 list<AudioRange>::const_iterator k = _ranges.begin ();
4598 while (k != _ranges.end()) {
4599 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4605 /* add it to our list if it overlaps at all */
4606 if (k != _ranges.end()) {
4611 _lines.push_back (n);
4617 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4621 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4623 return 1.0 - ((global_y - y_origin) / line->height());
4627 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4629 Drag::start_grab (event, cursor);
4631 /* Get line states before we start changing things */
4632 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4633 i->state = &i->line->get_state ();
4634 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4637 if (_ranges.empty()) {
4639 /* No selected time ranges: drag all points */
4640 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4641 uint32_t const N = i->line->npoints ();
4642 for (uint32_t j = 0; j < N; ++j) {
4643 i->points.push_back (i->line->nth (j));
4649 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4651 framecnt_t const half = (i->start + i->end) / 2;
4653 /* find the line that this audio range starts in */
4654 list<Line>::iterator j = _lines.begin();
4655 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4659 if (j != _lines.end()) {
4660 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4662 /* j is the line that this audio range starts in; fade into it;
4663 64 samples length plucked out of thin air.
4666 framepos_t a = i->start + 64;
4671 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4672 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4674 the_list->add (p, the_list->eval (p));
4675 the_list->add (q, the_list->eval (q));
4678 /* same thing for the end */
4681 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4685 if (j != _lines.end()) {
4686 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4688 /* j is the line that this audio range starts in; fade out of it;
4689 64 samples length plucked out of thin air.
4692 framepos_t b = i->end - 64;
4697 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4698 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4700 the_list->add (p, the_list->eval (p));
4701 the_list->add (q, the_list->eval (q));
4705 _nothing_to_drag = true;
4707 /* Find all the points that should be dragged and put them in the relevant
4708 points lists in the Line structs.
4711 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4713 uint32_t const N = i->line->npoints ();
4714 for (uint32_t j = 0; j < N; ++j) {
4716 /* here's a control point on this line */
4717 ControlPoint* p = i->line->nth (j);
4718 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4720 /* see if it's inside a range */
4721 list<AudioRange>::const_iterator k = _ranges.begin ();
4722 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4726 if (k != _ranges.end()) {
4727 /* dragging this point */
4728 _nothing_to_drag = false;
4729 i->points.push_back (p);
4735 if (_nothing_to_drag) {
4739 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4740 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4745 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4747 if (_nothing_to_drag) {
4751 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4752 float const f = y_fraction (l->line, _drags->current_pointer_y());
4753 /* we are ignoring x position for this drag, so we can just pass in anything */
4755 l->line->drag_motion (0, f, true, false, ignored);
4756 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4761 AutomationRangeDrag::finished (GdkEvent* event, bool)
4763 if (_nothing_to_drag) {
4767 motion (event, false);
4768 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4769 i->line->end_drag (false, 0);
4772 _editor->session()->commit_reversible_command ();
4776 AutomationRangeDrag::aborted (bool)
4778 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4783 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4786 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4787 layer = v->region()->layer ();
4788 initial_y = v->get_canvas_group()->position().y;
4789 initial_playlist = v->region()->playlist ();
4790 initial_position = v->region()->position ();
4791 initial_end = v->region()->position () + v->region()->length ();
4794 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4795 : Drag (e, i->canvas_item ())
4798 , _cumulative_dx (0)
4800 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4801 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4806 PatchChangeDrag::motion (GdkEvent* ev, bool)
4808 framepos_t f = adjusted_current_frame (ev);
4809 boost::shared_ptr<Region> r = _region_view->region ();
4810 f = max (f, r->position ());
4811 f = min (f, r->last_frame ());
4813 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4814 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4815 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4816 _cumulative_dx = dxu;
4820 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4822 if (!movement_occurred) {
4826 boost::shared_ptr<Region> r (_region_view->region ());
4827 framepos_t f = adjusted_current_frame (ev);
4828 f = max (f, r->position ());
4829 f = min (f, r->last_frame ());
4831 _region_view->move_patch_change (
4833 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4838 PatchChangeDrag::aborted (bool)
4840 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4844 PatchChangeDrag::setup_pointer_frame_offset ()
4846 boost::shared_ptr<Region> region = _region_view->region ();
4847 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4850 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4851 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4858 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4860 framepos_t const p = _region_view->region()->position ();
4861 double const y = _region_view->midi_view()->y_position ();
4863 x1 = max ((framepos_t) 0, x1 - p);
4864 x2 = max ((framepos_t) 0, x2 - p);
4865 y1 = max (0.0, y1 - y);
4866 y2 = max (0.0, y2 - y);
4868 _region_view->update_drag_selection (
4869 _editor->sample_to_pixel (x1),
4870 _editor->sample_to_pixel (x2),
4873 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4878 MidiRubberbandSelectDrag::deselect_things ()
4883 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4884 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4887 _vertical_only = true;
4891 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4893 double const y = _region_view->midi_view()->y_position ();
4895 y1 = max (0.0, y1 - y);
4896 y2 = max (0.0, y2 - y);
4898 _region_view->update_vertical_drag_selection (
4901 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4906 MidiVerticalSelectDrag::deselect_things ()
4911 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4912 : RubberbandSelectDrag (e, i)
4918 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4920 if (drag_in_progress) {
4921 /* We just want to select things at the end of the drag, not during it */
4925 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4927 _editor->begin_reversible_command (_("rubberband selection"));
4928 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4929 _editor->commit_reversible_command ();
4933 EditorRubberbandSelectDrag::deselect_things ()
4935 if (!getenv("ARDOUR_SAE")) {
4936 _editor->selection->clear_tracks();
4938 _editor->selection->clear_regions();
4939 _editor->selection->clear_points ();
4940 _editor->selection->clear_lines ();
4943 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4951 NoteCreateDrag::~NoteCreateDrag ()
4957 NoteCreateDrag::grid_frames (framepos_t t) const
4960 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4965 return _region_view->region_beats_to_region_frames (grid_beats);
4969 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4971 Drag::start_grab (event, cursor);
4973 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4975 framepos_t pf = _drags->current_pointer_frame ();
4976 framecnt_t const g = grid_frames (pf);
4978 /* Hack so that we always snap to the note that we are over, instead of snapping
4979 to the next one if we're more than halfway through the one we're over.
4981 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4985 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4987 MidiStreamView* sv = _region_view->midi_stream_view ();
4988 double const x = _editor->sample_to_pixel (_note[0]);
4989 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4991 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4992 _drag_rect->set_outline_all ();
4993 _drag_rect->set_outline_color (0xffffff99);
4994 _drag_rect->set_fill_color (0xffffff66);
4998 NoteCreateDrag::motion (GdkEvent* event, bool)
5000 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5001 double const x = _editor->sample_to_pixel (_note[1]);
5002 if (_note[1] > _note[0]) {
5003 _drag_rect->set_x1 (x);
5005 _drag_rect->set_x0 (x);
5010 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5012 if (!had_movement) {
5016 framepos_t const start = min (_note[0], _note[1]);
5017 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5019 framecnt_t const g = grid_frames (start);
5020 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5022 if (_editor->snap_mode() == SnapNormal && length < g) {
5023 length = g - one_tick;
5026 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5028 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5032 NoteCreateDrag::y_to_region (double y) const
5035 _region_view->get_canvas_group()->canvas_to_item (x, y);
5040 NoteCreateDrag::aborted (bool)
5045 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5050 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5054 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5056 Drag::start_grab (event, cursor);
5060 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5066 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5069 distance = _drags->current_pointer_x() - grab_x();
5070 len = ar->fade_in()->back()->when;
5072 distance = grab_x() - _drags->current_pointer_x();
5073 len = ar->fade_out()->back()->when;
5076 /* how long should it be ? */
5078 new_length = len + _editor->pixel_to_sample (distance);
5080 /* now check with the region that this is legal */
5082 new_length = ar->verify_xfade_bounds (new_length, start);
5085 arv->reset_fade_in_shape_width (ar, new_length);
5087 arv->reset_fade_out_shape_width (ar, new_length);
5092 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5098 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5101 distance = _drags->current_pointer_x() - grab_x();
5102 len = ar->fade_in()->back()->when;
5104 distance = grab_x() - _drags->current_pointer_x();
5105 len = ar->fade_out()->back()->when;
5108 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5110 _editor->begin_reversible_command ("xfade trim");
5111 ar->playlist()->clear_owned_changes ();
5114 ar->set_fade_in_length (new_length);
5116 ar->set_fade_out_length (new_length);
5119 /* Adjusting the xfade may affect other regions in the playlist, so we need
5120 to get undo Commands from the whole playlist rather than just the
5124 vector<Command*> cmds;
5125 ar->playlist()->rdiff (cmds);
5126 _editor->session()->add_commands (cmds);
5127 _editor->commit_reversible_command ();
5132 CrossfadeEdgeDrag::aborted (bool)
5135 arv->redraw_start_xfade ();
5137 arv->redraw_end_xfade ();