2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/scroll_group.h"
48 #include "audio_region_view.h"
49 #include "midi_region_view.h"
50 #include "ardour_ui.h"
51 #include "gui_thread.h"
52 #include "control_point.h"
54 #include "region_gain_line.h"
55 #include "editor_drag.h"
56 #include "audio_time_axis.h"
57 #include "midi_time_axis.h"
58 #include "selection.h"
59 #include "midi_selection.h"
60 #include "automation_time_axis.h"
62 #include "editor_cursors.h"
63 #include "mouse_cursors.h"
64 #include "note_base.h"
65 #include "patch_change.h"
66 #include "verbose_cursor.h"
69 using namespace ARDOUR;
72 using namespace Gtkmm2ext;
73 using namespace Editing;
74 using namespace ArdourCanvas;
76 using Gtkmm2ext::Keyboard;
78 double ControlPointDrag::_zero_gain_fraction = -1.0;
80 DragManager::DragManager (Editor* e)
83 , _current_pointer_frame (0)
87 DragManager::~DragManager ()
92 /** Call abort for each active drag */
98 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
103 if (!_drags.empty ()) {
104 _editor->set_follow_playhead (_old_follow_playhead, false);
113 DragManager::add (Drag* d)
115 d->set_manager (this);
116 _drags.push_back (d);
120 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
122 d->set_manager (this);
123 _drags.push_back (d);
128 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
130 /* Prevent follow playhead during the drag to be nice to the user */
131 _old_follow_playhead = _editor->follow_playhead ();
132 _editor->set_follow_playhead (false);
134 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
136 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
137 (*i)->start_grab (e, c);
141 /** Call end_grab for each active drag.
142 * @return true if any drag reported movement having occurred.
145 DragManager::end_grab (GdkEvent* e)
150 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
151 bool const t = (*i)->end_grab (e);
162 _editor->set_follow_playhead (_old_follow_playhead, false);
168 DragManager::mark_double_click ()
170 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
171 (*i)->set_double_click (true);
176 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
180 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
182 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
183 bool const t = (*i)->motion_handler (e, from_autoscroll);
184 /* run all handlers; return true if at least one of them
185 returns true (indicating that the event has been handled).
197 DragManager::window_motion_handler (GdkEvent* e, bool from_autoscroll)
201 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
203 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
204 bool const t = (*i)->motion_handler (e, from_autoscroll);
215 DragManager::have_item (ArdourCanvas::Item* i) const
217 list<Drag*>::const_iterator j = _drags.begin ();
218 while (j != _drags.end() && (*j)->item () != i) {
222 return j != _drags.end ();
225 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
228 , _pointer_frame_offset (0)
229 , _move_threshold_passed (false)
230 , _was_double_click (false)
231 , _raw_grab_frame (0)
233 , _last_pointer_frame (0)
239 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
252 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
254 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
256 if (Keyboard::is_button2_event (&event->button)) {
257 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
258 _y_constrained = true;
259 _x_constrained = false;
261 _y_constrained = false;
262 _x_constrained = true;
265 _x_constrained = false;
266 _y_constrained = false;
269 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
270 setup_pointer_frame_offset ();
271 _grab_frame = adjusted_frame (_raw_grab_frame, event);
272 _last_pointer_frame = _grab_frame;
273 _last_pointer_x = _grab_x;
274 _last_pointer_y = _grab_y;
280 /* CAIROCANVAS need a variant here that passes *cursor */
285 if (_editor->session() && _editor->session()->transport_rolling()) {
288 _was_rolling = false;
291 switch (_editor->snap_type()) {
292 case SnapToRegionStart:
293 case SnapToRegionEnd:
294 case SnapToRegionSync:
295 case SnapToRegionBoundary:
296 _editor->build_region_boundary_cache ();
303 /** Call to end a drag `successfully'. Ungrabs item and calls
304 * subclass' finished() method.
306 * @param event GDK event, or 0.
307 * @return true if some movement occurred, otherwise false.
310 Drag::end_grab (GdkEvent* event)
312 _editor->stop_canvas_autoscroll ();
316 finished (event, _move_threshold_passed);
318 _editor->verbose_cursor()->hide ();
320 return _move_threshold_passed;
324 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
328 if (f > _pointer_frame_offset) {
329 pos = f - _pointer_frame_offset;
333 _editor->snap_to_with_modifier (pos, event);
340 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
342 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
346 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
348 /* check to see if we have moved in any way that matters since the last motion event */
349 if (_move_threshold_passed &&
350 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
351 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
355 pair<framecnt_t, int> const threshold = move_threshold ();
357 bool const old_move_threshold_passed = _move_threshold_passed;
359 if (!from_autoscroll && !_move_threshold_passed) {
361 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
362 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
364 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
367 if (active (_editor->mouse_mode) && _move_threshold_passed) {
369 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
370 if (!from_autoscroll) {
371 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
374 if (!_editor->autoscroll_active() || from_autoscroll) {
375 motion (event, _move_threshold_passed != old_move_threshold_passed);
377 _last_pointer_x = _drags->current_pointer_x ();
378 _last_pointer_y = _drags->current_pointer_y ();
379 _last_pointer_frame = adjusted_current_frame (event);
388 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
396 aborted (_move_threshold_passed);
398 _editor->stop_canvas_autoscroll ();
399 _editor->verbose_cursor()->hide ();
403 Drag::show_verbose_cursor_time (framepos_t frame)
405 _editor->verbose_cursor()->set_time (
407 _drags->current_pointer_x() + 10,
408 _drags->current_pointer_y() + 10
411 _editor->verbose_cursor()->show ();
415 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
417 _editor->verbose_cursor()->show (xoffset);
419 _editor->verbose_cursor()->set_duration (
421 _drags->current_pointer_x() + 10,
422 _drags->current_pointer_y() + 10
427 Drag::show_verbose_cursor_text (string const & text)
429 _editor->verbose_cursor()->show ();
431 _editor->verbose_cursor()->set (
433 _drags->current_pointer_x() + 10,
434 _drags->current_pointer_y() + 10
438 boost::shared_ptr<Region>
439 Drag::add_midi_region (MidiTimeAxisView* view)
441 if (_editor->session()) {
442 const TempoMap& map (_editor->session()->tempo_map());
443 framecnt_t pos = grab_frame();
444 const Meter& m = map.meter_at (pos);
445 /* not that the frame rate used here can be affected by pull up/down which
448 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
449 return view->add_region (grab_frame(), len, true);
452 return boost::shared_ptr<Region>();
455 struct EditorOrderTimeAxisViewSorter {
456 bool operator() (TimeAxisView* a, TimeAxisView* b) {
457 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
458 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
460 return ra->route()->order_key () < rb->route()->order_key ();
464 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
468 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
470 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
471 as some of the regions we are dragging may be on such tracks.
474 TrackViewList track_views = _editor->track_views;
475 track_views.sort (EditorOrderTimeAxisViewSorter ());
477 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
478 _time_axis_views.push_back (*i);
480 TimeAxisView::Children children_list = (*i)->get_child_list ();
481 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
482 _time_axis_views.push_back (j->get());
486 /* the list of views can be empty at this point if this is a region list-insert drag
489 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
490 _views.push_back (DraggingView (*i, this));
493 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
497 RegionDrag::region_going_away (RegionView* v)
499 list<DraggingView>::iterator i = _views.begin ();
500 while (i != _views.end() && i->view != v) {
504 if (i != _views.end()) {
509 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
510 * or -1 if it is not found.
513 RegionDrag::find_time_axis_view (TimeAxisView* t) const
516 int const N = _time_axis_views.size ();
517 while (i < N && _time_axis_views[i] != t) {
528 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
529 : RegionDrag (e, i, p, v)
532 , _last_pointer_time_axis_view (0)
533 , _last_pointer_layer (0)
535 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
539 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
541 Drag::start_grab (event, cursor);
543 show_verbose_cursor_time (_last_frame_position);
545 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
547 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
548 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
553 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
555 /* compute the amount of pointer motion in frames, and where
556 the region would be if we moved it by that much.
558 *pending_region_position = adjusted_current_frame (event);
560 framepos_t sync_frame;
561 framecnt_t sync_offset;
564 sync_offset = _primary->region()->sync_offset (sync_dir);
566 /* we don't handle a sync point that lies before zero.
568 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
570 sync_frame = *pending_region_position + (sync_dir*sync_offset);
572 _editor->snap_to_with_modifier (sync_frame, event);
574 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
577 *pending_region_position = _last_frame_position;
580 if (*pending_region_position > max_framepos - _primary->region()->length()) {
581 *pending_region_position = _last_frame_position;
586 /* in locked edit mode, reverse the usual meaning of _x_constrained */
587 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
589 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
591 /* x movement since last time (in pixels) */
592 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
594 /* total x movement */
595 framecnt_t total_dx = *pending_region_position;
596 if (regions_came_from_canvas()) {
597 total_dx = total_dx - grab_frame ();
600 /* check that no regions have gone off the start of the session */
601 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
602 if ((i->view->region()->position() + total_dx) < 0) {
604 *pending_region_position = _last_frame_position;
609 _last_frame_position = *pending_region_position;
616 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
618 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
619 int const n = i->time_axis_view + delta_track;
620 if (n < 0 || n >= int (_time_axis_views.size())) {
621 /* off the top or bottom track */
625 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
626 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
627 /* not a track, or the wrong type */
631 double const l = i->layer + delta_layer;
633 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
634 mode to allow the user to place a region below another on layer 0.
636 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
637 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
638 If it has, the layers will be munged later anyway, so it's ok.
644 /* all regions being dragged are ok with this change */
649 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
651 double delta_layer = 0;
652 int delta_time_axis_view = 0;
654 assert (!_views.empty ());
656 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
658 /* Find the TimeAxisView that the pointer is now over */
659 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (_drags->current_pointer_y ());
660 TimeAxisView* tv = r.first;
662 if (tv && tv->view()) {
663 double layer = r.second;
665 if (first_move && tv->view()->layer_display() == Stacked) {
666 tv->view()->set_layer_display (Expanded);
669 /* Here's the current pointer position in terms of time axis view and layer */
670 int const current_pointer_time_axis_view = find_time_axis_view (tv);
671 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
673 /* Work out the change in y */
675 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
676 delta_layer = current_pointer_layer - _last_pointer_layer;
679 /* Work out the change in x */
680 framepos_t pending_region_position;
681 double const x_delta = compute_x_delta (event, &pending_region_position);
683 /* Verify change in y */
684 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
685 /* this y movement is not allowed, so do no y movement this time */
686 delta_time_axis_view = 0;
690 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
691 /* haven't reached next snap point, and we're not switching
692 trackviews nor layers. nothing to do.
697 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
699 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
701 RegionView* rv = i->view;
703 if (rv->region()->locked() || rv->region()->video_locked()) {
709 rv->fake_set_opaque (true);
711 /* reparent the regionview into a group above all
715 ArdourCanvas::Group* rvg = rv->get_canvas_group();
716 Duple rv_canvas_offset = rvg->parent()->item_to_canvas (Duple (0,0));
717 Duple dmg_canvas_offset = _editor->_drag_motion_group->item_to_canvas (Duple (0,0));
718 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
719 /* move the item so that it continues to appear at the
720 same location now that its parent has changed.
722 rvg->move (rv_canvas_offset - dmg_canvas_offset);
725 /* If we have moved tracks, we'll fudge the layer delta so that the
726 region gets moved back onto layer 0 on its new track; this avoids
727 confusion when dragging regions from non-zero layers onto different
730 double this_delta_layer = delta_layer;
731 if (delta_time_axis_view != 0) {
732 this_delta_layer = - i->layer;
737 int track_index = i->time_axis_view + delta_time_axis_view;
739 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
743 /* The TimeAxisView that this region is now over */
744 TimeAxisView* current_tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
746 /* Ensure it is moved from stacked -> expanded if appropriate */
747 if (current_tv->view()->layer_display() == Stacked) {
748 current_tv->view()->set_layer_display (Expanded);
751 /* We're only allowed to go -ve in layer on Expanded views */
752 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
753 this_delta_layer = - i->layer;
757 rv->set_height (current_tv->view()->child_height ());
759 /* Update show/hidden status as the region view may have come from a hidden track,
760 or have moved to one.
762 if (current_tv->hidden ()) {
763 rv->get_canvas_group()->hide ();
765 rv->get_canvas_group()->show ();
768 /* Update the DraggingView */
769 i->time_axis_view += delta_time_axis_view;
770 i->layer += this_delta_layer;
773 _editor->mouse_brush_insert_region (rv, pending_region_position);
777 /* Get the y coordinate of the top of the track that this region is now over */
778 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
780 /* And adjust for the layer that it should be on */
781 StreamView* cv = current_tv->view ();
782 switch (cv->layer_display ()) {
786 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
789 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
793 /* need to get the parent of the regionview
794 * canvas group and get its position in
795 * equivalent coordinate space as the trackview
796 * we are now dragging over.
799 /* Now move the region view */
800 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->item_to_canvas (Duple (0, 0)).y);
804 /* Only move the region into the empty dropzone at the bottom if the pointer
808 if (_drags->current_pointer_y() >= _editor->get_trackview_group()->item_to_canvas (Duple (0,0)).y) {
811 TimeAxisView* last = _time_axis_views.back();
812 track_origin = last->canvas_display()->item_to_canvas (track_origin);
813 track_origin.y += last->effective_height();
814 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->item_to_canvas (Duple (0,0)).y);
815 i->time_axis_view = -1;
819 } /* foreach region */
821 _total_x_delta += x_delta;
823 if (x_delta != 0 && !_brushing) {
824 show_verbose_cursor_time (_last_frame_position);
827 _last_pointer_time_axis_view += delta_time_axis_view;
828 _last_pointer_layer += delta_layer;
832 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
834 if (_copy && first_move) {
836 /* duplicate the regionview(s) and region(s) */
838 list<DraggingView> new_regionviews;
840 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
842 RegionView* rv = i->view;
843 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
844 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
846 const boost::shared_ptr<const Region> original = rv->region();
847 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
848 region_copy->set_position (original->position());
852 boost::shared_ptr<AudioRegion> audioregion_copy
853 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
855 nrv = new AudioRegionView (*arv, audioregion_copy);
857 boost::shared_ptr<MidiRegion> midiregion_copy
858 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
859 nrv = new MidiRegionView (*mrv, midiregion_copy);
864 nrv->get_canvas_group()->show ();
865 new_regionviews.push_back (DraggingView (nrv, this));
867 /* swap _primary to the copy */
869 if (rv == _primary) {
873 /* ..and deselect the one we copied */
875 rv->set_selected (false);
878 if (!new_regionviews.empty()) {
880 /* reflect the fact that we are dragging the copies */
882 _views = new_regionviews;
884 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
888 RegionMotionDrag::motion (event, first_move);
892 RegionMotionDrag::finished (GdkEvent *, bool)
894 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
899 if ((*i)->view()->layer_display() == Expanded) {
900 (*i)->view()->set_layer_display (Stacked);
906 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
908 RegionMotionDrag::finished (ev, movement_occurred);
910 if (!movement_occurred) {
914 if (was_double_click() && !_views.empty()) {
915 DraggingView dv = _views.front();
916 dv.view->show_region_editor ();
923 /* reverse this here so that we have the correct logic to finalize
927 if (Config->get_edit_mode() == Lock) {
928 _x_constrained = !_x_constrained;
931 assert (!_views.empty ());
933 /* We might have hidden region views so that they weren't visible during the drag
934 (when they have been reparented). Now everything can be shown again, as region
935 views are back in their track parent groups.
937 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
938 i->view->get_canvas_group()->show ();
941 bool const changed_position = (_last_frame_position != _primary->region()->position());
942 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
943 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
963 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
967 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region)
969 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
974 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
975 list<boost::shared_ptr<AudioTrack> > audio_tracks;
976 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
977 return _editor->axis_view_from_route (audio_tracks.front());
979 ChanCount one_midi_port (DataType::MIDI, 1);
980 list<boost::shared_ptr<MidiTrack> > midi_tracks;
981 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
982 return _editor->axis_view_from_route (midi_tracks.front());
985 error << _("Could not create new track after region placed in the drop zone") << endmsg;
991 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
993 RegionSelection new_views;
994 PlaylistSet modified_playlists;
995 RouteTimeAxisView* new_time_axis_view = 0;
998 /* all changes were made during motion event handlers */
1000 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1004 _editor->commit_reversible_command ();
1008 if (_x_constrained) {
1009 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1011 _editor->begin_reversible_command (Operations::region_copy);
1014 /* insert the regions into their new playlists */
1015 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1017 RouteTimeAxisView* dest_rtv = 0;
1019 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1025 if (changed_position && !_x_constrained) {
1026 where = i->view->region()->position() - drag_delta;
1028 where = i->view->region()->position();
1031 if (i->time_axis_view < 0) {
1032 if (!new_time_axis_view) {
1033 new_time_axis_view = create_destination_time_axis (i->view->region());
1035 dest_rtv = new_time_axis_view;
1037 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1040 if (dest_rtv != 0) {
1041 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1042 if (new_view != 0) {
1043 new_views.push_back (new_view);
1047 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1048 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1051 list<DraggingView>::const_iterator next = i;
1057 /* If we've created new regions either by copying or moving
1058 to a new track, we want to replace the old selection with the new ones
1061 if (new_views.size() > 0) {
1062 _editor->selection->set (new_views);
1065 /* write commands for the accumulated diffs for all our modified playlists */
1066 add_stateful_diff_commands_for_playlists (modified_playlists);
1068 _editor->commit_reversible_command ();
1072 RegionMoveDrag::finished_no_copy (
1073 bool const changed_position,
1074 bool const changed_tracks,
1075 framecnt_t const drag_delta
1078 RegionSelection new_views;
1079 PlaylistSet modified_playlists;
1080 PlaylistSet frozen_playlists;
1081 set<RouteTimeAxisView*> views_to_update;
1082 RouteTimeAxisView* new_time_axis_view = 0;
1085 /* all changes were made during motion event handlers */
1086 _editor->commit_reversible_command ();
1090 if (_x_constrained) {
1091 _editor->begin_reversible_command (_("fixed time region drag"));
1093 _editor->begin_reversible_command (Operations::region_drag);
1096 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1098 RegionView* rv = i->view;
1099 RouteTimeAxisView* dest_rtv = 0;
1101 if (rv->region()->locked() || rv->region()->video_locked()) {
1106 if (i->time_axis_view < 0) {
1107 if (!new_time_axis_view) {
1108 new_time_axis_view = create_destination_time_axis (rv->region());
1110 dest_rtv = new_time_axis_view;
1112 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1117 double const dest_layer = i->layer;
1119 views_to_update.insert (dest_rtv);
1123 if (changed_position && !_x_constrained) {
1124 where = rv->region()->position() - drag_delta;
1126 where = rv->region()->position();
1129 if (changed_tracks) {
1131 /* insert into new playlist */
1133 RegionView* new_view = insert_region_into_playlist (
1134 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1137 if (new_view == 0) {
1142 new_views.push_back (new_view);
1144 /* remove from old playlist */
1146 /* the region that used to be in the old playlist is not
1147 moved to the new one - we use a copy of it. as a result,
1148 any existing editor for the region should no longer be
1151 rv->hide_region_editor();
1152 rv->fake_set_opaque (false);
1154 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1158 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1160 /* this movement may result in a crossfade being modified, or a layering change,
1161 so we need to get undo data from the playlist as well as the region.
1164 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1166 playlist->clear_changes ();
1169 rv->region()->clear_changes ();
1172 motion on the same track. plonk the previously reparented region
1173 back to its original canvas group (its streamview).
1174 No need to do anything for copies as they are fake regions which will be deleted.
1177 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1178 rv->get_canvas_group()->set_y_position (i->initial_y);
1181 /* just change the model */
1182 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1183 playlist->set_layer (rv->region(), dest_layer);
1186 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1188 r = frozen_playlists.insert (playlist);
1191 playlist->freeze ();
1194 rv->region()->set_position (where);
1196 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1199 if (changed_tracks) {
1201 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1202 was selected in all of them, then removing it from a playlist will have removed all
1203 trace of it from _views (i.e. there were N regions selected, we removed 1,
1204 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1205 corresponding regionview, and _views is now empty).
1207 This could have invalidated any and all iterators into _views.
1209 The heuristic we use here is: if the region selection is empty, break out of the loop
1210 here. if the region selection is not empty, then restart the loop because we know that
1211 we must have removed at least the region(view) we've just been working on as well as any
1212 that we processed on previous iterations.
1214 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1215 we can just iterate.
1219 if (_views.empty()) {
1230 /* If we've created new regions either by copying or moving
1231 to a new track, we want to replace the old selection with the new ones
1234 if (new_views.size() > 0) {
1235 _editor->selection->set (new_views);
1238 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1242 /* write commands for the accumulated diffs for all our modified playlists */
1243 add_stateful_diff_commands_for_playlists (modified_playlists);
1245 _editor->commit_reversible_command ();
1247 /* We have futzed with the layering of canvas items on our streamviews.
1248 If any region changed layer, this will have resulted in the stream
1249 views being asked to set up their region views, and all will be well.
1250 If not, we might now have badly-ordered region views. Ask the StreamViews
1251 involved to sort themselves out, just in case.
1254 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1255 (*i)->view()->playlist_layered ((*i)->track ());
1259 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1260 * @param region Region to remove.
1261 * @param playlist playlist To remove from.
1262 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1263 * that clear_changes () is only called once per playlist.
1266 RegionMoveDrag::remove_region_from_playlist (
1267 boost::shared_ptr<Region> region,
1268 boost::shared_ptr<Playlist> playlist,
1269 PlaylistSet& modified_playlists
1272 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1275 playlist->clear_changes ();
1278 playlist->remove_region (region);
1282 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1283 * clearing the playlist's diff history first if necessary.
1284 * @param region Region to insert.
1285 * @param dest_rtv Destination RouteTimeAxisView.
1286 * @param dest_layer Destination layer.
1287 * @param where Destination position.
1288 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1289 * that clear_changes () is only called once per playlist.
1290 * @return New RegionView, or 0 if no insert was performed.
1293 RegionMoveDrag::insert_region_into_playlist (
1294 boost::shared_ptr<Region> region,
1295 RouteTimeAxisView* dest_rtv,
1298 PlaylistSet& modified_playlists
1301 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1302 if (!dest_playlist) {
1306 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1307 _new_region_view = 0;
1308 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1310 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1311 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1313 dest_playlist->clear_changes ();
1316 dest_playlist->add_region (region, where);
1318 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1319 dest_playlist->set_layer (region, dest_layer);
1324 assert (_new_region_view);
1326 return _new_region_view;
1330 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1332 _new_region_view = rv;
1336 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1338 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1339 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1341 _editor->session()->add_command (c);
1350 RegionMoveDrag::aborted (bool movement_occurred)
1354 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1361 RegionMotionDrag::aborted (movement_occurred);
1366 RegionMotionDrag::aborted (bool)
1368 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1370 StreamView* sview = (*i)->view();
1373 if (sview->layer_display() == Expanded) {
1374 sview->set_layer_display (Stacked);
1379 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1380 RegionView* rv = i->view;
1381 TimeAxisView* tv = &(rv->get_time_axis_view ());
1382 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1384 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1385 rv->get_canvas_group()->set_y_position (0);
1387 rv->fake_set_opaque (false);
1388 rv->move (-_total_x_delta, 0);
1389 rv->set_height (rtv->view()->child_height ());
1393 /** @param b true to brush, otherwise false.
1394 * @param c true to make copies of the regions being moved, otherwise false.
1396 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1397 : RegionMotionDrag (e, i, p, v, b),
1400 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1403 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1404 if (rtv && rtv->is_track()) {
1405 speed = rtv->track()->speed ();
1408 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1412 RegionMoveDrag::setup_pointer_frame_offset ()
1414 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1417 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1418 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1420 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1422 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1423 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1425 _primary = v->view()->create_region_view (r, false, false);
1427 _primary->get_canvas_group()->show ();
1428 _primary->set_position (pos, 0);
1429 _views.push_back (DraggingView (_primary, this));
1431 _last_frame_position = pos;
1433 _item = _primary->get_canvas_group ();
1437 RegionInsertDrag::finished (GdkEvent *, bool)
1439 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1441 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1442 _primary->get_canvas_group()->set_y_position (0);
1444 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1446 _editor->begin_reversible_command (Operations::insert_region);
1447 playlist->clear_changes ();
1448 playlist->add_region (_primary->region (), _last_frame_position);
1449 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1450 _editor->commit_reversible_command ();
1458 RegionInsertDrag::aborted (bool)
1465 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1466 : RegionMoveDrag (e, i, p, v, false, false)
1468 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1471 struct RegionSelectionByPosition {
1472 bool operator() (RegionView*a, RegionView* b) {
1473 return a->region()->position () < b->region()->position();
1478 RegionSpliceDrag::motion (GdkEvent* event, bool)
1480 /* Which trackview is this ? */
1482 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1483 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1485 /* The region motion is only processed if the pointer is over
1489 if (!tv || !tv->is_track()) {
1490 /* To make sure we hide the verbose canvas cursor when the mouse is
1491 not held over and audiotrack.
1493 _editor->verbose_cursor()->hide ();
1499 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1505 RegionSelection copy (_editor->selection->regions);
1507 RegionSelectionByPosition cmp;
1510 framepos_t const pf = adjusted_current_frame (event);
1512 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1514 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1520 boost::shared_ptr<Playlist> playlist;
1522 if ((playlist = atv->playlist()) == 0) {
1526 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1531 if (pf < (*i)->region()->last_frame() + 1) {
1535 if (pf > (*i)->region()->first_frame()) {
1541 playlist->shuffle ((*i)->region(), dir);
1546 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1548 RegionMoveDrag::finished (event, movement_occurred);
1552 RegionSpliceDrag::aborted (bool)
1557 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1559 _view (dynamic_cast<MidiTimeAxisView*> (v))
1561 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1567 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1570 _region = add_midi_region (_view);
1571 _view->playlist()->freeze ();
1574 framepos_t const f = adjusted_current_frame (event);
1575 if (f < grab_frame()) {
1576 _region->set_position (f);
1579 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1580 so that if this region is duplicated, its duplicate starts on
1581 a snap point rather than 1 frame after a snap point. Otherwise things get
1582 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1583 place snapped notes at the start of the region.
1586 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1587 _region->set_length (len < 1 ? 1 : len);
1593 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1595 if (!movement_occurred) {
1596 add_midi_region (_view);
1598 _view->playlist()->thaw ();
1603 RegionCreateDrag::aborted (bool)
1606 _view->playlist()->thaw ();
1612 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1616 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1620 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1622 Gdk::Cursor* cursor;
1623 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1625 float x_fraction = cnote->mouse_x_fraction ();
1627 if (x_fraction > 0.0 && x_fraction < 0.25) {
1628 cursor = _editor->cursors()->left_side_trim;
1630 cursor = _editor->cursors()->right_side_trim;
1633 Drag::start_grab (event, cursor);
1635 region = &cnote->region_view();
1637 double const region_start = region->get_position_pixels();
1638 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1640 if (grab_x() <= middle_point) {
1641 cursor = _editor->cursors()->left_side_trim;
1644 cursor = _editor->cursors()->right_side_trim;
1650 if (event->motion.state & Keyboard::PrimaryModifier) {
1656 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1658 if (ms.size() > 1) {
1659 /* has to be relative, may make no sense otherwise */
1663 /* select this note; if it is already selected, preserve the existing selection,
1664 otherwise make this note the only one selected.
1666 region->note_selected (cnote, cnote->selected ());
1668 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1669 MidiRegionSelection::iterator next;
1672 (*r)->begin_resizing (at_front);
1678 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1680 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1681 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1682 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1684 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1689 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1691 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1692 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1693 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1695 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1700 NoteResizeDrag::aborted (bool)
1702 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1703 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1704 (*r)->abort_resizing ();
1708 AVDraggingView::AVDraggingView (RegionView* v)
1711 initial_position = v->region()->position ();
1714 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1717 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1720 TrackViewList empty;
1722 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1723 std::list<RegionView*> views = rs.by_layer();
1725 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1726 RegionView* rv = (*i);
1727 if (!rv->region()->video_locked()) {
1730 _views.push_back (AVDraggingView (rv));
1735 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1737 Drag::start_grab (event);
1738 if (_editor->session() == 0) {
1742 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1743 _max_backwards_drag = (
1744 ARDOUR_UI::instance()->video_timeline->get_duration()
1745 + ARDOUR_UI::instance()->video_timeline->get_offset()
1746 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1749 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1750 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1751 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1754 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1757 Timecode::Time timecode;
1758 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1759 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);
1760 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1761 _editor->verbose_cursor()->show ();
1765 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1767 if (_editor->session() == 0) {
1770 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1774 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1775 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
1777 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1778 dt = - _max_backwards_drag;
1781 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1782 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1784 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1785 RegionView* rv = i->view;
1786 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1789 rv->fake_set_opaque (true);
1790 rv->region()->clear_changes ();
1791 rv->region()->suspend_property_changes();
1793 rv->region()->set_position(i->initial_position + dt);
1794 rv->region_changed(ARDOUR::Properties::position);
1797 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1798 Timecode::Time timecode;
1799 Timecode::Time timediff;
1801 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1802 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1803 snprintf (buf, sizeof (buf),
1804 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1805 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1806 , _("Video Start:"),
1807 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1809 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1811 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1812 _editor->verbose_cursor()->show ();
1816 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1818 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1822 if (!movement_occurred || ! _editor->session()) {
1826 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1828 _editor->begin_reversible_command (_("Move Video"));
1830 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1831 ARDOUR_UI::instance()->video_timeline->save_undo();
1832 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1833 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1835 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1836 i->view->drag_end();
1837 i->view->fake_set_opaque (false);
1838 i->view->region()->resume_property_changes ();
1840 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1843 _editor->session()->maybe_update_session_range(
1844 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1845 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1849 _editor->commit_reversible_command ();
1853 VideoTimeLineDrag::aborted (bool)
1855 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1858 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1859 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1861 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1862 i->view->region()->resume_property_changes ();
1863 i->view->region()->set_position(i->initial_position);
1867 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1868 : RegionDrag (e, i, p, v)
1869 , _preserve_fade_anchor (preserve_fade_anchor)
1870 , _jump_position_when_done (false)
1872 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1876 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1879 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1880 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1882 if (tv && tv->is_track()) {
1883 speed = tv->track()->speed();
1886 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1887 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1888 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1890 framepos_t const pf = adjusted_current_frame (event);
1892 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1893 /* Move the contents of the region around without changing the region bounds */
1894 _operation = ContentsTrim;
1895 Drag::start_grab (event, _editor->cursors()->trimmer);
1897 /* These will get overridden for a point trim.*/
1898 if (pf < (region_start + region_length/2)) {
1899 /* closer to front */
1900 _operation = StartTrim;
1901 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1904 _operation = EndTrim;
1905 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1909 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1910 _jump_position_when_done = true;
1913 switch (_operation) {
1915 show_verbose_cursor_time (region_start);
1916 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1917 i->view->trim_front_starting ();
1921 show_verbose_cursor_time (region_end);
1924 show_verbose_cursor_time (pf);
1928 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1929 i->view->region()->suspend_property_changes ();
1934 TrimDrag::motion (GdkEvent* event, bool first_move)
1936 RegionView* rv = _primary;
1939 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1940 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1941 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1942 frameoffset_t frame_delta = 0;
1944 if (tv && tv->is_track()) {
1945 speed = tv->track()->speed();
1948 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1954 switch (_operation) {
1956 trim_type = "Region start trim";
1959 trim_type = "Region end trim";
1962 trim_type = "Region content trim";
1969 _editor->begin_reversible_command (trim_type);
1971 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1972 RegionView* rv = i->view;
1973 rv->fake_set_opaque (false);
1974 rv->enable_display (false);
1975 rv->region()->playlist()->clear_owned_changes ();
1977 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1980 arv->temporarily_hide_envelope ();
1984 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1985 insert_result = _editor->motion_frozen_playlists.insert (pl);
1987 if (insert_result.second) {
1993 bool non_overlap_trim = false;
1995 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1996 non_overlap_trim = true;
1999 /* contstrain trim to fade length */
2000 if (_preserve_fade_anchor) {
2001 switch (_operation) {
2003 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2004 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2006 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2007 if (ar->locked()) continue;
2008 framecnt_t len = ar->fade_in()->back()->when;
2009 if (len < dt) dt = min(dt, len);
2013 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2014 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2016 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2017 if (ar->locked()) continue;
2018 framecnt_t len = ar->fade_out()->back()->when;
2019 if (len < -dt) dt = max(dt, -len);
2028 switch (_operation) {
2030 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2031 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2032 if (changed && _preserve_fade_anchor) {
2033 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2035 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2036 framecnt_t len = ar->fade_in()->back()->when;
2037 framecnt_t diff = ar->first_frame() - i->initial_position;
2038 framepos_t new_length = len - diff;
2039 i->anchored_fade_length = min (ar->length(), new_length);
2040 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2041 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2048 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2049 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2050 if (changed && _preserve_fade_anchor) {
2051 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2053 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2054 framecnt_t len = ar->fade_out()->back()->when;
2055 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2056 framepos_t new_length = len + diff;
2057 i->anchored_fade_length = min (ar->length(), new_length);
2058 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2059 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2067 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2069 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2070 i->view->move_contents (frame_delta);
2076 switch (_operation) {
2078 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2081 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2084 // show_verbose_cursor_time (frame_delta);
2091 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2093 if (movement_occurred) {
2094 motion (event, false);
2096 if (_operation == StartTrim) {
2097 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2099 /* This must happen before the region's StatefulDiffCommand is created, as it may
2100 `correct' (ahem) the region's _start from being negative to being zero. It
2101 needs to be zero in the undo record.
2103 i->view->trim_front_ending ();
2105 if (_preserve_fade_anchor) {
2106 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2108 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2109 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2110 ar->set_fade_in_length(i->anchored_fade_length);
2111 ar->set_fade_in_active(true);
2114 if (_jump_position_when_done) {
2115 i->view->region()->set_position (i->initial_position);
2118 } else if (_operation == EndTrim) {
2119 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2120 if (_preserve_fade_anchor) {
2121 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2123 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2124 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2125 ar->set_fade_out_length(i->anchored_fade_length);
2126 ar->set_fade_out_active(true);
2129 if (_jump_position_when_done) {
2130 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2135 if (!_views.empty()) {
2136 if (_operation == StartTrim) {
2137 _editor->maybe_locate_with_edit_preroll(
2138 _views.begin()->view->region()->position());
2140 if (_operation == EndTrim) {
2141 _editor->maybe_locate_with_edit_preroll(
2142 _views.begin()->view->region()->position() +
2143 _views.begin()->view->region()->length());
2147 if (!_editor->selection->selected (_primary)) {
2148 _primary->thaw_after_trim ();
2151 set<boost::shared_ptr<Playlist> > diffed_playlists;
2153 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2154 i->view->thaw_after_trim ();
2155 i->view->enable_display (true);
2156 i->view->fake_set_opaque (true);
2158 /* Trimming one region may affect others on the playlist, so we need
2159 to get undo Commands from the whole playlist rather than just the
2160 region. Use diffed_playlists to make sure we don't diff a given
2161 playlist more than once.
2163 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2164 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2165 vector<Command*> cmds;
2167 _editor->session()->add_commands (cmds);
2168 diffed_playlists.insert (p);
2173 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2177 _editor->motion_frozen_playlists.clear ();
2178 _editor->commit_reversible_command();
2181 /* no mouse movement */
2182 _editor->point_trim (event, adjusted_current_frame (event));
2185 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2186 if (_operation == StartTrim) {
2187 i->view->trim_front_ending ();
2190 i->view->region()->resume_property_changes ();
2195 TrimDrag::aborted (bool movement_occurred)
2197 /* Our motion method is changing model state, so use the Undo system
2198 to cancel. Perhaps not ideal, as this will leave an Undo point
2199 behind which may be slightly odd from the user's point of view.
2204 if (movement_occurred) {
2208 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2209 i->view->region()->resume_property_changes ();
2214 TrimDrag::setup_pointer_frame_offset ()
2216 list<DraggingView>::iterator i = _views.begin ();
2217 while (i != _views.end() && i->view != _primary) {
2221 if (i == _views.end()) {
2225 switch (_operation) {
2227 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2230 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2237 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2241 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2242 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2247 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2249 Drag::start_grab (event, cursor);
2250 show_verbose_cursor_time (adjusted_current_frame(event));
2254 MeterMarkerDrag::setup_pointer_frame_offset ()
2256 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2260 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2262 if (!_marker->meter().movable()) {
2268 // create a dummy marker for visual representation of moving the
2269 // section, because whether its a copy or not, we're going to
2270 // leave or lose the original marker (leave if its a copy; lose if its
2271 // not, because we'll remove it from the map).
2273 MeterSection section (_marker->meter());
2275 if (!section.movable()) {
2280 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2282 _marker = new MeterMarker (
2284 *_editor->meter_group,
2285 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2287 *new MeterSection (_marker->meter())
2290 /* use the new marker for the grab */
2291 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2294 TempoMap& map (_editor->session()->tempo_map());
2295 /* get current state */
2296 before_state = &map.get_state();
2297 /* remove the section while we drag it */
2298 map.remove_meter (section, true);
2302 framepos_t const pf = adjusted_current_frame (event);
2303 _marker->set_position (pf);
2304 show_verbose_cursor_time (pf);
2308 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2310 if (!movement_occurred) {
2311 if (was_double_click()) {
2312 _editor->edit_meter_marker (*_marker);
2317 if (!_marker->meter().movable()) {
2321 motion (event, false);
2323 Timecode::BBT_Time when;
2325 TempoMap& map (_editor->session()->tempo_map());
2326 map.bbt_time (last_pointer_frame(), when);
2328 if (_copy == true) {
2329 _editor->begin_reversible_command (_("copy meter mark"));
2330 XMLNode &before = map.get_state();
2331 map.add_meter (_marker->meter(), when);
2332 XMLNode &after = map.get_state();
2333 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2334 _editor->commit_reversible_command ();
2337 _editor->begin_reversible_command (_("move meter mark"));
2339 /* we removed it before, so add it back now */
2341 map.add_meter (_marker->meter(), when);
2342 XMLNode &after = map.get_state();
2343 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2344 _editor->commit_reversible_command ();
2347 // delete the dummy marker we used for visual representation while moving.
2348 // a new visual marker will show up automatically.
2353 MeterMarkerDrag::aborted (bool moved)
2355 _marker->set_position (_marker->meter().frame ());
2358 TempoMap& map (_editor->session()->tempo_map());
2359 /* we removed it before, so add it back now */
2360 map.add_meter (_marker->meter(), _marker->meter().frame());
2361 // delete the dummy marker we used for visual representation while moving.
2362 // a new visual marker will show up automatically.
2367 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2371 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2373 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2378 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2380 Drag::start_grab (event, cursor);
2381 show_verbose_cursor_time (adjusted_current_frame (event));
2385 TempoMarkerDrag::setup_pointer_frame_offset ()
2387 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2391 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2393 if (!_marker->tempo().movable()) {
2399 // create a dummy marker for visual representation of moving the
2400 // section, because whether its a copy or not, we're going to
2401 // leave or lose the original marker (leave if its a copy; lose if its
2402 // not, because we'll remove it from the map).
2404 // create a dummy marker for visual representation of moving the copy.
2405 // The actual copying is not done before we reach the finish callback.
2408 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2410 TempoSection section (_marker->tempo());
2412 _marker = new TempoMarker (
2414 *_editor->tempo_group,
2415 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2417 *new TempoSection (_marker->tempo())
2420 /* use the new marker for the grab */
2421 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2424 TempoMap& map (_editor->session()->tempo_map());
2425 /* get current state */
2426 before_state = &map.get_state();
2427 /* remove the section while we drag it */
2428 map.remove_tempo (section, true);
2432 framepos_t const pf = adjusted_current_frame (event);
2433 _marker->set_position (pf);
2434 show_verbose_cursor_time (pf);
2438 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2440 if (!movement_occurred) {
2441 if (was_double_click()) {
2442 _editor->edit_tempo_marker (*_marker);
2447 if (!_marker->tempo().movable()) {
2451 motion (event, false);
2453 TempoMap& map (_editor->session()->tempo_map());
2454 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2455 Timecode::BBT_Time when;
2457 map.bbt_time (beat_time, when);
2459 if (_copy == true) {
2460 _editor->begin_reversible_command (_("copy tempo mark"));
2461 XMLNode &before = map.get_state();
2462 map.add_tempo (_marker->tempo(), when);
2463 XMLNode &after = map.get_state();
2464 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2465 _editor->commit_reversible_command ();
2468 _editor->begin_reversible_command (_("move tempo mark"));
2469 /* we removed it before, so add it back now */
2470 map.add_tempo (_marker->tempo(), when);
2471 XMLNode &after = map.get_state();
2472 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2473 _editor->commit_reversible_command ();
2476 // delete the dummy marker we used for visual representation while moving.
2477 // a new visual marker will show up automatically.
2482 TempoMarkerDrag::aborted (bool moved)
2484 _marker->set_position (_marker->tempo().frame());
2486 TempoMap& map (_editor->session()->tempo_map());
2487 /* we removed it before, so add it back now */
2488 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2489 // delete the dummy marker we used for visual representation while moving.
2490 // a new visual marker will show up automatically.
2495 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2496 : Drag (e, &c.track_canvas_item())
2500 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2503 /** Do all the things we do when dragging the playhead to make it look as though
2504 * we have located, without actually doing the locate (because that would cause
2505 * the diskstream buffers to be refilled, which is too slow).
2508 CursorDrag::fake_locate (framepos_t t)
2510 _editor->playhead_cursor->set_position (t);
2512 Session* s = _editor->session ();
2513 if (s->timecode_transmission_suspended ()) {
2514 framepos_t const f = _editor->playhead_cursor->current_frame ();
2515 /* This is asynchronous so it will be sent "now"
2517 s->send_mmc_locate (f);
2518 /* These are synchronous and will be sent during the next
2521 s->queue_full_time_code ();
2522 s->queue_song_position_pointer ();
2525 show_verbose_cursor_time (t);
2526 _editor->UpdateAllTransportClocks (t);
2530 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2532 Drag::start_grab (event, c);
2534 _grab_zoom = _editor->samples_per_pixel;
2536 framepos_t where = _editor->canvas_event_sample (event);
2538 _editor->snap_to_with_modifier (where, event);
2540 _editor->_dragging_playhead = true;
2542 Session* s = _editor->session ();
2544 /* grab the track canvas item as well */
2546 _cursor.track_canvas_item().grab();
2549 if (_was_rolling && _stop) {
2553 if (s->is_auditioning()) {
2554 s->cancel_audition ();
2558 if (AudioEngine::instance()->connected()) {
2560 /* do this only if we're the engine is connected
2561 * because otherwise this request will never be
2562 * serviced and we'll busy wait forever. likewise,
2563 * notice if we are disconnected while waiting for the
2564 * request to be serviced.
2567 s->request_suspend_timecode_transmission ();
2568 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2569 /* twiddle our thumbs */
2574 fake_locate (where);
2578 CursorDrag::motion (GdkEvent* event, bool)
2580 framepos_t const adjusted_frame = adjusted_current_frame (event);
2581 if (adjusted_frame != last_pointer_frame()) {
2582 fake_locate (adjusted_frame);
2587 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2589 _editor->_dragging_playhead = false;
2591 _cursor.track_canvas_item().ungrab();
2593 if (!movement_occurred && _stop) {
2597 motion (event, false);
2599 Session* s = _editor->session ();
2601 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2602 _editor->_pending_locate_request = true;
2603 s->request_resume_timecode_transmission ();
2608 CursorDrag::aborted (bool)
2610 _cursor.track_canvas_item().ungrab();
2612 if (_editor->_dragging_playhead) {
2613 _editor->session()->request_resume_timecode_transmission ();
2614 _editor->_dragging_playhead = false;
2617 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2620 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2621 : RegionDrag (e, i, p, v)
2623 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2627 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2629 Drag::start_grab (event, cursor);
2631 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2632 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2634 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2638 FadeInDrag::setup_pointer_frame_offset ()
2640 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2641 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2642 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2646 FadeInDrag::motion (GdkEvent* event, bool)
2648 framecnt_t fade_length;
2650 framepos_t const pos = adjusted_current_frame (event);
2652 boost::shared_ptr<Region> region = _primary->region ();
2654 if (pos < (region->position() + 64)) {
2655 fade_length = 64; // this should be a minimum defined somewhere
2656 } else if (pos > region->last_frame()) {
2657 fade_length = region->length();
2659 fade_length = pos - region->position();
2662 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2664 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2670 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2673 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2677 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2679 if (!movement_occurred) {
2683 framecnt_t fade_length;
2685 framepos_t const pos = adjusted_current_frame (event);
2687 boost::shared_ptr<Region> region = _primary->region ();
2689 if (pos < (region->position() + 64)) {
2690 fade_length = 64; // this should be a minimum defined somewhere
2691 } else if (pos > region->last_frame()) {
2692 fade_length = region->length();
2694 fade_length = pos - region->position();
2697 _editor->begin_reversible_command (_("change fade in length"));
2699 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2701 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2707 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2708 XMLNode &before = alist->get_state();
2710 tmp->audio_region()->set_fade_in_length (fade_length);
2711 tmp->audio_region()->set_fade_in_active (true);
2713 XMLNode &after = alist->get_state();
2714 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2717 _editor->commit_reversible_command ();
2721 FadeInDrag::aborted (bool)
2723 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2724 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2730 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2734 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2735 : RegionDrag (e, i, p, v)
2737 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2741 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2743 Drag::start_grab (event, cursor);
2745 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2746 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2748 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2752 FadeOutDrag::setup_pointer_frame_offset ()
2754 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2755 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2756 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2760 FadeOutDrag::motion (GdkEvent* event, bool)
2762 framecnt_t fade_length;
2764 framepos_t const pos = adjusted_current_frame (event);
2766 boost::shared_ptr<Region> region = _primary->region ();
2768 if (pos > (region->last_frame() - 64)) {
2769 fade_length = 64; // this should really be a minimum fade defined somewhere
2771 else if (pos < region->position()) {
2772 fade_length = region->length();
2775 fade_length = region->last_frame() - pos;
2778 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2780 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2786 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2789 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2793 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2795 if (!movement_occurred) {
2799 framecnt_t fade_length;
2801 framepos_t const pos = adjusted_current_frame (event);
2803 boost::shared_ptr<Region> region = _primary->region ();
2805 if (pos > (region->last_frame() - 64)) {
2806 fade_length = 64; // this should really be a minimum fade defined somewhere
2808 else if (pos < region->position()) {
2809 fade_length = region->length();
2812 fade_length = region->last_frame() - pos;
2815 _editor->begin_reversible_command (_("change fade out length"));
2817 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2819 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2825 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2826 XMLNode &before = alist->get_state();
2828 tmp->audio_region()->set_fade_out_length (fade_length);
2829 tmp->audio_region()->set_fade_out_active (true);
2831 XMLNode &after = alist->get_state();
2832 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2835 _editor->commit_reversible_command ();
2839 FadeOutDrag::aborted (bool)
2841 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2842 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2848 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2852 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2855 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2857 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2860 _points.push_back (ArdourCanvas::Duple (0, 0));
2861 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2864 MarkerDrag::~MarkerDrag ()
2866 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2871 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2873 location = new Location (*l);
2874 markers.push_back (m);
2879 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2881 Drag::start_grab (event, cursor);
2885 Location *location = _editor->find_location_from_marker (_marker, is_start);
2886 _editor->_dragging_edit_point = true;
2888 update_item (location);
2890 // _drag_line->show();
2891 // _line->raise_to_top();
2894 show_verbose_cursor_time (location->start());
2896 show_verbose_cursor_time (location->end());
2899 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2902 case Selection::Toggle:
2903 /* we toggle on the button release */
2905 case Selection::Set:
2906 if (!_editor->selection->selected (_marker)) {
2907 _editor->selection->set (_marker);
2910 case Selection::Extend:
2912 Locations::LocationList ll;
2913 list<Marker*> to_add;
2915 _editor->selection->markers.range (s, e);
2916 s = min (_marker->position(), s);
2917 e = max (_marker->position(), e);
2920 if (e < max_framepos) {
2923 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2924 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2925 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2928 to_add.push_back (lm->start);
2931 to_add.push_back (lm->end);
2935 if (!to_add.empty()) {
2936 _editor->selection->add (to_add);
2940 case Selection::Add:
2941 _editor->selection->add (_marker);
2945 /* Set up copies for us to manipulate during the drag
2948 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2950 Location* l = _editor->find_location_from_marker (*i, is_start);
2957 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2959 /* range: check that the other end of the range isn't
2962 CopiedLocationInfo::iterator x;
2963 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2964 if (*(*x).location == *l) {
2968 if (x == _copied_locations.end()) {
2969 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2971 (*x).markers.push_back (*i);
2972 (*x).move_both = true;
2980 MarkerDrag::setup_pointer_frame_offset ()
2983 Location *location = _editor->find_location_from_marker (_marker, is_start);
2984 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2988 MarkerDrag::motion (GdkEvent* event, bool)
2990 framecnt_t f_delta = 0;
2992 bool move_both = false;
2993 Location *real_location;
2994 Location *copy_location = 0;
2996 framepos_t const newframe = adjusted_current_frame (event);
2997 framepos_t next = newframe;
2999 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3003 CopiedLocationInfo::iterator x;
3005 /* find the marker we're dragging, and compute the delta */
3007 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3009 copy_location = (*x).location;
3011 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3013 /* this marker is represented by this
3014 * CopiedLocationMarkerInfo
3017 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3022 if (real_location->is_mark()) {
3023 f_delta = newframe - copy_location->start();
3027 switch (_marker->type()) {
3028 case Marker::SessionStart:
3029 case Marker::RangeStart:
3030 case Marker::LoopStart:
3031 case Marker::PunchIn:
3032 f_delta = newframe - copy_location->start();
3035 case Marker::SessionEnd:
3036 case Marker::RangeEnd:
3037 case Marker::LoopEnd:
3038 case Marker::PunchOut:
3039 f_delta = newframe - copy_location->end();
3042 /* what kind of marker is this ? */
3051 if (x == _copied_locations.end()) {
3052 /* hmm, impossible - we didn't find the dragged marker */
3056 /* now move them all */
3058 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3060 copy_location = x->location;
3062 /* call this to find out if its the start or end */
3064 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3068 if (real_location->locked()) {
3072 if (copy_location->is_mark()) {
3076 copy_location->set_start (copy_location->start() + f_delta);
3080 framepos_t new_start = copy_location->start() + f_delta;
3081 framepos_t new_end = copy_location->end() + f_delta;
3083 if (is_start) { // start-of-range marker
3085 if (move_both || (*x).move_both) {
3086 copy_location->set_start (new_start);
3087 copy_location->set_end (new_end);
3088 } else if (new_start < copy_location->end()) {
3089 copy_location->set_start (new_start);
3090 } else if (newframe > 0) {
3091 _editor->snap_to (next, 1, true);
3092 copy_location->set_end (next);
3093 copy_location->set_start (newframe);
3096 } else { // end marker
3098 if (move_both || (*x).move_both) {
3099 copy_location->set_end (new_end);
3100 copy_location->set_start (new_start);
3101 } else if (new_end > copy_location->start()) {
3102 copy_location->set_end (new_end);
3103 } else if (newframe > 0) {
3104 _editor->snap_to (next, -1, true);
3105 copy_location->set_start (next);
3106 copy_location->set_end (newframe);
3111 update_item (copy_location);
3113 /* now lookup the actual GUI items used to display this
3114 * location and move them to wherever the copy of the location
3115 * is now. This means that the logic in ARDOUR::Location is
3116 * still enforced, even though we are not (yet) modifying
3117 * the real Location itself.
3120 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3123 lm->set_position (copy_location->start(), copy_location->end());
3128 assert (!_copied_locations.empty());
3130 show_verbose_cursor_time (newframe);
3134 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3136 if (!movement_occurred) {
3138 if (was_double_click()) {
3139 _editor->rename_marker (_marker);
3143 /* just a click, do nothing but finish
3144 off the selection process
3147 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3150 case Selection::Set:
3151 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3152 _editor->selection->set (_marker);
3156 case Selection::Toggle:
3157 /* we toggle on the button release, click only */
3158 _editor->selection->toggle (_marker);
3161 case Selection::Extend:
3162 case Selection::Add:
3169 _editor->_dragging_edit_point = false;
3171 _editor->begin_reversible_command ( _("move marker") );
3172 XMLNode &before = _editor->session()->locations()->get_state();
3174 MarkerSelection::iterator i;
3175 CopiedLocationInfo::iterator x;
3178 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3179 x != _copied_locations.end() && i != _editor->selection->markers.end();
3182 Location * location = _editor->find_location_from_marker (*i, is_start);
3186 if (location->locked()) {
3190 if (location->is_mark()) {
3191 location->set_start (((*x).location)->start());
3193 location->set (((*x).location)->start(), ((*x).location)->end());
3198 XMLNode &after = _editor->session()->locations()->get_state();
3199 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3200 _editor->commit_reversible_command ();
3204 MarkerDrag::aborted (bool)
3210 MarkerDrag::update_item (Location*)
3215 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3217 _cumulative_x_drag (0),
3218 _cumulative_y_drag (0)
3220 if (_zero_gain_fraction < 0.0) {
3221 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3224 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3226 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3232 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3234 Drag::start_grab (event, _editor->cursors()->fader);
3236 // start the grab at the center of the control point so
3237 // the point doesn't 'jump' to the mouse after the first drag
3238 _fixed_grab_x = _point->get_x();
3239 _fixed_grab_y = _point->get_y();
3241 float const fraction = 1 - (_point->get_y() / _point->line().height());
3243 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3245 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3246 event->button.x + 10, event->button.y + 10);
3248 _editor->verbose_cursor()->show ();
3250 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3252 if (!_point->can_slide ()) {
3253 _x_constrained = true;
3258 ControlPointDrag::motion (GdkEvent* event, bool)
3260 double dx = _drags->current_pointer_x() - last_pointer_x();
3261 double dy = _drags->current_pointer_y() - last_pointer_y();
3263 if (event->button.state & Keyboard::SecondaryModifier) {
3268 /* coordinate in pixels relative to the start of the region (for region-based automation)
3269 or track (for track-based automation) */
3270 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3271 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3273 // calculate zero crossing point. back off by .01 to stay on the
3274 // positive side of zero
3275 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3277 // make sure we hit zero when passing through
3278 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3282 if (_x_constrained) {
3285 if (_y_constrained) {
3289 _cumulative_x_drag = cx - _fixed_grab_x;
3290 _cumulative_y_drag = cy - _fixed_grab_y;
3294 cy = min ((double) _point->line().height(), cy);
3296 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3298 if (!_x_constrained) {
3299 _editor->snap_to_with_modifier (cx_frames, event);
3302 cx_frames = min (cx_frames, _point->line().maximum_time());
3304 float const fraction = 1.0 - (cy / _point->line().height());
3306 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3308 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3312 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3314 if (!movement_occurred) {
3318 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3319 _editor->reset_point_selection ();
3323 motion (event, false);
3326 _point->line().end_drag (_pushing, _final_index);
3327 _editor->session()->commit_reversible_command ();
3331 ControlPointDrag::aborted (bool)
3333 _point->line().reset ();
3337 ControlPointDrag::active (Editing::MouseMode m)
3339 if (m == Editing::MouseGain) {
3340 /* always active in mouse gain */
3344 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3345 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3348 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3351 _cumulative_y_drag (0)
3353 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3357 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3359 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3362 _item = &_line->grab_item ();
3364 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3365 origin, and ditto for y.
3368 double cx = event->button.x;
3369 double cy = event->button.y;
3371 _line->parent_group().canvas_to_item (cx, cy);
3373 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3378 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3379 /* no adjacent points */
3383 Drag::start_grab (event, _editor->cursors()->fader);
3385 /* store grab start in parent frame */
3390 double fraction = 1.0 - (cy / _line->height());
3392 _line->start_drag_line (before, after, fraction);
3394 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3395 event->button.x + 10, event->button.y + 10);
3397 _editor->verbose_cursor()->show ();
3401 LineDrag::motion (GdkEvent* event, bool)
3403 double dy = _drags->current_pointer_y() - last_pointer_y();
3405 if (event->button.state & Keyboard::SecondaryModifier) {
3409 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3411 _cumulative_y_drag = cy - _fixed_grab_y;
3414 cy = min ((double) _line->height(), cy);
3416 double const fraction = 1.0 - (cy / _line->height());
3419 /* we are ignoring x position for this drag, so we can just pass in anything */
3420 _line->drag_motion (0, fraction, true, false, ignored);
3422 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3426 LineDrag::finished (GdkEvent* event, bool movement_occured)
3428 if (movement_occured) {
3429 motion (event, false);
3430 _line->end_drag (false, 0);
3432 /* add a new control point on the line */
3434 AutomationTimeAxisView* atv;
3436 _line->end_drag (false, 0);
3438 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3439 framepos_t where = _editor->window_event_sample (event, 0, 0);
3440 atv->add_automation_event (event, where, event->button.y, false);
3444 _editor->session()->commit_reversible_command ();
3448 LineDrag::aborted (bool)
3453 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3456 _cumulative_x_drag (0)
3458 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3462 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3464 Drag::start_grab (event);
3466 _line = reinterpret_cast<Line*> (_item);
3469 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3471 double cx = event->button.x;
3472 double cy = event->button.y;
3474 _item->parent()->canvas_to_item (cx, cy);
3476 /* store grab start in parent frame */
3477 _region_view_grab_x = cx;
3479 _before = *(float*) _item->get_data ("position");
3481 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3483 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3487 FeatureLineDrag::motion (GdkEvent*, bool)
3489 double dx = _drags->current_pointer_x() - last_pointer_x();
3491 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3493 _cumulative_x_drag += dx;
3495 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3504 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3506 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3508 float *pos = new float;
3511 _line->set_data ("position", pos);
3517 FeatureLineDrag::finished (GdkEvent*, bool)
3519 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3520 _arv->update_transient(_before, _before);
3524 FeatureLineDrag::aborted (bool)
3529 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3531 , _vertical_only (false)
3533 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3537 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3539 Drag::start_grab (event);
3540 show_verbose_cursor_time (adjusted_current_frame (event));
3544 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3551 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3553 framepos_t grab = grab_frame ();
3554 if (Config->get_rubberbanding_snaps_to_grid ()) {
3555 _editor->snap_to_with_modifier (grab, event);
3558 /* base start and end on initial click position */
3568 if (_drags->current_pointer_y() < grab_y()) {
3569 y1 = _drags->current_pointer_y();
3572 y2 = _drags->current_pointer_y();
3577 if (start != end || y1 != y2) {
3579 double x1 = _editor->sample_to_pixel (start);
3580 double x2 = _editor->sample_to_pixel (end);
3581 const double min_dimension = 2.0;
3583 if (_vertical_only) {
3584 /* fixed 10 pixel width */
3588 x2 = min (x1 - min_dimension, x2);
3590 x2 = max (x1 + min_dimension, x2);
3595 y2 = min (y1 - min_dimension, y2);
3597 y2 = max (y1 + min_dimension, y2);
3600 /* translate rect into item space and set */
3602 Rect r (x1, y1, x2, y2);
3604 _editor->rubberband_rect->set (_editor->rubberband_rect->canvas_to_item (r));
3605 _editor->rubberband_rect->show();
3606 _editor->rubberband_rect->raise_to_top();
3608 show_verbose_cursor_time (pf);
3610 do_select_things (event, true);
3615 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3620 if (grab_frame() < last_pointer_frame()) {
3622 x2 = last_pointer_frame ();
3625 x1 = last_pointer_frame ();
3631 if (_drags->current_pointer_y() < grab_y()) {
3632 y1 = _drags->current_pointer_y();
3635 y2 = _drags->current_pointer_y();
3639 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3643 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3645 if (movement_occurred) {
3647 motion (event, false);
3648 do_select_things (event, false);
3654 bool do_deselect = true;
3655 MidiTimeAxisView* mtv;
3657 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3659 if (_editor->selection->empty()) {
3660 /* nothing selected */
3661 add_midi_region (mtv);
3662 do_deselect = false;
3666 /* do not deselect if Primary or Tertiary (toggle-select or
3667 * extend-select are pressed.
3670 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3671 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3678 _editor->rubberband_rect->hide();
3682 RubberbandSelectDrag::aborted (bool)
3684 _editor->rubberband_rect->hide ();
3687 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3688 : RegionDrag (e, i, p, v)
3690 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3694 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3696 Drag::start_grab (event, cursor);
3698 show_verbose_cursor_time (adjusted_current_frame (event));
3702 TimeFXDrag::motion (GdkEvent* event, bool)
3704 RegionView* rv = _primary;
3705 StreamView* cv = rv->get_time_axis_view().view ();
3707 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3708 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3709 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3711 framepos_t const pf = adjusted_current_frame (event);
3713 if (pf > rv->region()->position()) {
3714 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3717 show_verbose_cursor_time (pf);
3721 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3723 _primary->get_time_axis_view().hide_timestretch ();
3725 if (!movement_occurred) {
3729 if (last_pointer_frame() < _primary->region()->position()) {
3730 /* backwards drag of the left edge - not usable */
3734 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3736 float percentage = (double) newlen / (double) _primary->region()->length();
3738 #ifndef USE_RUBBERBAND
3739 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3740 if (_primary->region()->data_type() == DataType::AUDIO) {
3741 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3745 if (!_editor->get_selection().regions.empty()) {
3746 /* primary will already be included in the selection, and edit
3747 group shared editing will propagate selection across
3748 equivalent regions, so just use the current region
3752 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3753 error << _("An error occurred while executing time stretch operation") << endmsg;
3759 TimeFXDrag::aborted (bool)
3761 _primary->get_time_axis_view().hide_timestretch ();
3764 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3767 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3771 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3773 Drag::start_grab (event);
3777 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3779 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3783 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3785 if (movement_occurred && _editor->session()) {
3786 /* make sure we stop */
3787 _editor->session()->request_transport_speed (0.0);
3792 ScrubDrag::aborted (bool)
3797 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3802 , _original_pointer_time_axis (-1)
3803 , _last_pointer_time_axis (-1)
3804 , _time_selection_at_start (!_editor->get_selection().time.empty())
3806 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3808 if (_time_selection_at_start) {
3809 start_at_start = _editor->get_selection().time.start();
3810 end_at_start = _editor->get_selection().time.end_frame();
3815 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3817 if (_editor->session() == 0) {
3821 Gdk::Cursor* cursor = 0;
3823 switch (_operation) {
3824 case CreateSelection:
3825 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3830 cursor = _editor->cursors()->selector;
3831 Drag::start_grab (event, cursor);
3834 case SelectionStartTrim:
3835 if (_editor->clicked_axisview) {
3836 _editor->clicked_axisview->order_selection_trims (_item, true);
3838 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3841 case SelectionEndTrim:
3842 if (_editor->clicked_axisview) {
3843 _editor->clicked_axisview->order_selection_trims (_item, false);
3845 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3849 Drag::start_grab (event, cursor);
3852 case SelectionExtend:
3853 Drag::start_grab (event, cursor);
3857 if (_operation == SelectionMove) {
3858 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3860 show_verbose_cursor_time (adjusted_current_frame (event));
3863 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3867 SelectionDrag::setup_pointer_frame_offset ()
3869 switch (_operation) {
3870 case CreateSelection:
3871 _pointer_frame_offset = 0;
3874 case SelectionStartTrim:
3876 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3879 case SelectionEndTrim:
3880 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3883 case SelectionExtend:
3889 SelectionDrag::motion (GdkEvent* event, bool first_move)
3891 framepos_t start = 0;
3893 framecnt_t length = 0;
3894 framecnt_t distance = 0;
3896 framepos_t const pending_position = adjusted_current_frame (event);
3898 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
3902 switch (_operation) {
3903 case CreateSelection:
3905 framepos_t grab = grab_frame ();
3908 grab = adjusted_current_frame (event, false);
3909 if (grab < pending_position) {
3910 _editor->snap_to (grab, -1);
3912 _editor->snap_to (grab, 1);
3916 if (pending_position < grab) {
3917 start = pending_position;
3920 end = pending_position;
3924 /* first drag: Either add to the selection
3925 or create a new selection
3932 /* adding to the selection */
3933 _editor->set_selected_track_as_side_effect (Selection::Add);
3934 _editor->clicked_selection = _editor->selection->add (start, end);
3941 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3942 _editor->set_selected_track_as_side_effect (Selection::Set);
3945 _editor->clicked_selection = _editor->selection->set (start, end);
3949 /* select all tracks within the rectangle that we've marked out so far */
3950 TrackViewList to_be_added_to_selection;
3951 TrackViewList to_be_removed_from_selection;
3952 TrackViewList& all_tracks (_editor->track_views);
3954 // /* convert grab_y and current_pointer_y into offsets within the trackview group */
3956 ArdourCanvas::Duple const top_of_trackviews_canvas = _editor->get_trackview_group()->item_to_canvas (ArdourCanvas::Duple (0, 0));
3957 ArdourCanvas::Coord const top = grab_y() - top_of_trackviews_canvas.y;
3958 ArdourCanvas::Coord const bottom = _drags->current_pointer_y() - top_of_trackviews_canvas.y;
3960 if (top >= 0 && bottom >= 0) {
3962 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
3964 if ((*i)->covered_by_y_range (top, bottom)) {
3965 if (!(*i)->get_selected()) {
3966 to_be_added_to_selection.push_back (*i);
3969 if ((*i)->get_selected()) {
3970 to_be_removed_from_selection.push_back (*i);
3975 if (!to_be_added_to_selection.empty()) {
3976 _editor->selection->add (to_be_added_to_selection);
3979 if (!to_be_removed_from_selection.empty()) {
3980 _editor->selection->remove (to_be_removed_from_selection);
3986 case SelectionStartTrim:
3988 start = _editor->selection->time[_editor->clicked_selection].start;
3989 end = _editor->selection->time[_editor->clicked_selection].end;
3991 if (pending_position > end) {
3994 start = pending_position;
3998 case SelectionEndTrim:
4000 start = _editor->selection->time[_editor->clicked_selection].start;
4001 end = _editor->selection->time[_editor->clicked_selection].end;
4003 if (pending_position < start) {
4006 end = pending_position;
4013 start = _editor->selection->time[_editor->clicked_selection].start;
4014 end = _editor->selection->time[_editor->clicked_selection].end;
4016 length = end - start;
4017 distance = pending_position - start;
4018 start = pending_position;
4019 _editor->snap_to (start);
4021 end = start + length;
4025 case SelectionExtend:
4030 switch (_operation) {
4032 if (_time_selection_at_start) {
4033 _editor->selection->move_time (distance);
4037 _editor->selection->replace (_editor->clicked_selection, start, end);
4041 if (_operation == SelectionMove) {
4042 show_verbose_cursor_time(start);
4044 show_verbose_cursor_time(pending_position);
4049 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4051 Session* s = _editor->session();
4053 if (movement_occurred) {
4054 motion (event, false);
4055 /* XXX this is not object-oriented programming at all. ick */
4056 if (_editor->selection->time.consolidate()) {
4057 _editor->selection->TimeChanged ();
4060 /* XXX what if its a music time selection? */
4062 if ( s->get_play_range() && s->transport_rolling() ) {
4063 s->request_play_range (&_editor->selection->time, true);
4065 if (Config->get_always_play_range() && !s->transport_rolling()) {
4066 s->request_locate (_editor->get_selection().time.start());
4072 /* just a click, no pointer movement.
4075 if (_operation == SelectionExtend) {
4076 if (_time_selection_at_start) {
4077 framepos_t pos = adjusted_current_frame (event, false);
4078 framepos_t start = min (pos, start_at_start);
4079 framepos_t end = max (pos, end_at_start);
4080 _editor->selection->set (start, end);
4083 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4084 if (_editor->clicked_selection) {
4085 _editor->selection->remove (_editor->clicked_selection);
4088 if (!_editor->clicked_selection) {
4089 _editor->selection->clear_time();
4094 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4095 _editor->selection->set (_editor->clicked_axisview);
4098 if (s && s->get_play_range () && s->transport_rolling()) {
4099 s->request_stop (false, false);
4104 _editor->stop_canvas_autoscroll ();
4105 _editor->clicked_selection = 0;
4109 SelectionDrag::aborted (bool)
4114 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4119 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4121 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4122 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4123 physical_screen_height (_editor->get_window())));
4124 _drag_rect->hide ();
4126 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4127 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4131 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4133 if (_editor->session() == 0) {
4137 Gdk::Cursor* cursor = 0;
4139 if (!_editor->temp_location) {
4140 _editor->temp_location = new Location (*_editor->session());
4143 switch (_operation) {
4144 case CreateRangeMarker:
4145 case CreateTransportMarker:
4146 case CreateCDMarker:
4148 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4153 cursor = _editor->cursors()->selector;
4157 Drag::start_grab (event, cursor);
4159 show_verbose_cursor_time (adjusted_current_frame (event));
4163 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4165 framepos_t start = 0;
4167 ArdourCanvas::Rectangle *crect;
4169 switch (_operation) {
4170 case CreateRangeMarker:
4171 crect = _editor->range_bar_drag_rect;
4173 case CreateTransportMarker:
4174 crect = _editor->transport_bar_drag_rect;
4176 case CreateCDMarker:
4177 crect = _editor->cd_marker_bar_drag_rect;
4180 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4185 framepos_t const pf = adjusted_current_frame (event);
4187 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4188 framepos_t grab = grab_frame ();
4189 _editor->snap_to (grab);
4191 if (pf < grab_frame()) {
4199 /* first drag: Either add to the selection
4200 or create a new selection.
4205 _editor->temp_location->set (start, end);
4209 update_item (_editor->temp_location);
4211 //_drag_rect->raise_to_top();
4217 _editor->temp_location->set (start, end);
4219 double x1 = _editor->sample_to_pixel (start);
4220 double x2 = _editor->sample_to_pixel (end);
4224 update_item (_editor->temp_location);
4227 show_verbose_cursor_time (pf);
4232 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4234 Location * newloc = 0;
4238 if (movement_occurred) {
4239 motion (event, false);
4242 switch (_operation) {
4243 case CreateRangeMarker:
4244 case CreateCDMarker:
4246 _editor->begin_reversible_command (_("new range marker"));
4247 XMLNode &before = _editor->session()->locations()->get_state();
4248 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4249 if (_operation == CreateCDMarker) {
4250 flags = Location::IsRangeMarker | Location::IsCDMarker;
4251 _editor->cd_marker_bar_drag_rect->hide();
4254 flags = Location::IsRangeMarker;
4255 _editor->range_bar_drag_rect->hide();
4257 newloc = new Location (
4258 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4261 _editor->session()->locations()->add (newloc, true);
4262 XMLNode &after = _editor->session()->locations()->get_state();
4263 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4264 _editor->commit_reversible_command ();
4268 case CreateTransportMarker:
4269 // popup menu to pick loop or punch
4270 _editor->new_transport_marker_context_menu (&event->button, _item);
4276 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4278 if (_operation == CreateTransportMarker) {
4280 /* didn't drag, so just locate */
4282 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4284 } else if (_operation == CreateCDMarker) {
4286 /* didn't drag, but mark is already created so do
4289 } else { /* operation == CreateRangeMarker */
4295 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4297 if (end == max_framepos) {
4298 end = _editor->session()->current_end_frame ();
4301 if (start == max_framepos) {
4302 start = _editor->session()->current_start_frame ();
4305 switch (_editor->mouse_mode) {
4307 /* find the two markers on either side and then make the selection from it */
4308 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4312 /* find the two markers on either side of the click and make the range out of it */
4313 _editor->selection->set (start, end);
4322 _editor->stop_canvas_autoscroll ();
4326 RangeMarkerBarDrag::aborted (bool)
4332 RangeMarkerBarDrag::update_item (Location* location)
4334 double const x1 = _editor->sample_to_pixel (location->start());
4335 double const x2 = _editor->sample_to_pixel (location->end());
4337 _drag_rect->set_x0 (x1);
4338 _drag_rect->set_x1 (x2);
4341 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4345 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4349 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4351 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4352 Drag::start_grab (event, _editor->cursors()->zoom_out);
4355 Drag::start_grab (event, _editor->cursors()->zoom_in);
4359 show_verbose_cursor_time (adjusted_current_frame (event));
4363 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4368 framepos_t const pf = adjusted_current_frame (event);
4370 framepos_t grab = grab_frame ();
4371 _editor->snap_to_with_modifier (grab, event);
4373 /* base start and end on initial click position */
4385 _editor->zoom_rect->show();
4386 _editor->zoom_rect->raise_to_top();
4389 _editor->reposition_zoom_rect(start, end);
4391 show_verbose_cursor_time (pf);
4396 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4398 if (movement_occurred) {
4399 motion (event, false);
4401 if (grab_frame() < last_pointer_frame()) {
4402 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4404 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4407 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4408 _editor->tav_zoom_step (_zoom_out);
4410 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4414 _editor->zoom_rect->hide();
4418 MouseZoomDrag::aborted (bool)
4420 _editor->zoom_rect->hide ();
4423 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4425 , _cumulative_dx (0)
4426 , _cumulative_dy (0)
4428 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4430 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4432 _region = &_primary->region_view ();
4433 _note_height = _region->midi_stream_view()->note_height ();
4437 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4439 Drag::start_grab (event);
4441 if (!(_was_selected = _primary->selected())) {
4443 /* tertiary-click means extend selection - we'll do that on button release,
4444 so don't add it here, because otherwise we make it hard to figure
4445 out the "extend-to" range.
4448 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4451 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4454 _region->note_selected (_primary, true);
4456 _region->unique_select (_primary);
4462 /** @return Current total drag x change in frames */
4464 NoteDrag::total_dx () const
4467 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4469 /* primary note time */
4470 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4472 /* new time of the primary note in session frames */
4473 frameoffset_t st = n + dx;
4475 framepos_t const rp = _region->region()->position ();
4477 /* prevent the note being dragged earlier than the region's position */
4480 /* snap and return corresponding delta */
4481 return _region->snap_frame_to_frame (st - rp) + rp - n;
4484 /** @return Current total drag y change in note number */
4486 NoteDrag::total_dy () const
4488 MidiStreamView* msv = _region->midi_stream_view ();
4489 double const y = _region->midi_view()->y_position ();
4490 /* new current note */
4491 uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4493 n = max (msv->lowest_note(), n);
4494 n = min (msv->highest_note(), n);
4495 /* and work out delta */
4496 return n - msv->y_to_note (grab_y() - y);
4500 NoteDrag::motion (GdkEvent *, bool)
4502 /* Total change in x and y since the start of the drag */
4503 frameoffset_t const dx = total_dx ();
4504 int8_t const dy = total_dy ();
4506 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4507 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4508 double const tdy = -dy * _note_height - _cumulative_dy;
4511 _cumulative_dx += tdx;
4512 _cumulative_dy += tdy;
4514 int8_t note_delta = total_dy();
4516 _region->move_selection (tdx, tdy, note_delta);
4518 /* the new note value may be the same as the old one, but we
4519 * don't know what that means because the selection may have
4520 * involved more than one note and we might be doing something
4521 * odd with them. so show the note value anyway, always.
4525 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4527 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4528 (int) floor ((double)new_note));
4530 show_verbose_cursor_text (buf);
4535 NoteDrag::finished (GdkEvent* ev, bool moved)
4538 /* no motion - select note */
4540 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4541 _editor->current_mouse_mode() == Editing::MouseDraw) {
4543 if (_was_selected) {
4544 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4546 _region->note_deselected (_primary);
4549 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4550 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4552 if (!extend && !add && _region->selection_size() > 1) {
4553 _region->unique_select (_primary);
4554 } else if (extend) {
4555 _region->note_selected (_primary, true, true);
4557 /* it was added during button press */
4562 _region->note_dropped (_primary, total_dx(), total_dy());
4567 NoteDrag::aborted (bool)
4572 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4573 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4574 : Drag (editor, atv->base_item ())
4576 , _nothing_to_drag (false)
4578 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4579 y_origin = atv->y_position();
4580 setup (atv->lines ());
4583 /** Make an AutomationRangeDrag for region gain lines */
4584 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4585 : Drag (editor, rv->get_canvas_group ())
4587 , _nothing_to_drag (false)
4589 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4591 list<boost::shared_ptr<AutomationLine> > lines;
4592 lines.push_back (rv->get_gain_line ());
4593 y_origin = rv->get_time_axis_view().y_position();
4597 /** @param lines AutomationLines to drag.
4598 * @param offset Offset from the session start to the points in the AutomationLines.
4601 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4603 /* find the lines that overlap the ranges being dragged */
4604 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4605 while (i != lines.end ()) {
4606 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4609 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4611 /* check this range against all the AudioRanges that we are using */
4612 list<AudioRange>::const_iterator k = _ranges.begin ();
4613 while (k != _ranges.end()) {
4614 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4620 /* add it to our list if it overlaps at all */
4621 if (k != _ranges.end()) {
4626 _lines.push_back (n);
4632 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4636 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4638 return 1.0 - ((global_y - y_origin) / line->height());
4642 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4644 Drag::start_grab (event, cursor);
4646 /* Get line states before we start changing things */
4647 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4648 i->state = &i->line->get_state ();
4649 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4652 if (_ranges.empty()) {
4654 /* No selected time ranges: drag all points */
4655 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4656 uint32_t const N = i->line->npoints ();
4657 for (uint32_t j = 0; j < N; ++j) {
4658 i->points.push_back (i->line->nth (j));
4664 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4666 framecnt_t const half = (i->start + i->end) / 2;
4668 /* find the line that this audio range starts in */
4669 list<Line>::iterator j = _lines.begin();
4670 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4674 if (j != _lines.end()) {
4675 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4677 /* j is the line that this audio range starts in; fade into it;
4678 64 samples length plucked out of thin air.
4681 framepos_t a = i->start + 64;
4686 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4687 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4689 the_list->add (p, the_list->eval (p));
4690 the_list->add (q, the_list->eval (q));
4693 /* same thing for the end */
4696 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4700 if (j != _lines.end()) {
4701 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4703 /* j is the line that this audio range starts in; fade out of it;
4704 64 samples length plucked out of thin air.
4707 framepos_t b = i->end - 64;
4712 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4713 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4715 the_list->add (p, the_list->eval (p));
4716 the_list->add (q, the_list->eval (q));
4720 _nothing_to_drag = true;
4722 /* Find all the points that should be dragged and put them in the relevant
4723 points lists in the Line structs.
4726 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4728 uint32_t const N = i->line->npoints ();
4729 for (uint32_t j = 0; j < N; ++j) {
4731 /* here's a control point on this line */
4732 ControlPoint* p = i->line->nth (j);
4733 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4735 /* see if it's inside a range */
4736 list<AudioRange>::const_iterator k = _ranges.begin ();
4737 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4741 if (k != _ranges.end()) {
4742 /* dragging this point */
4743 _nothing_to_drag = false;
4744 i->points.push_back (p);
4750 if (_nothing_to_drag) {
4754 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4755 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4760 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4762 if (_nothing_to_drag) {
4766 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4767 float const f = y_fraction (l->line, _drags->current_pointer_y());
4768 /* we are ignoring x position for this drag, so we can just pass in anything */
4770 l->line->drag_motion (0, f, true, false, ignored);
4771 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4776 AutomationRangeDrag::finished (GdkEvent* event, bool)
4778 if (_nothing_to_drag) {
4782 motion (event, false);
4783 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4784 i->line->end_drag (false, 0);
4787 _editor->session()->commit_reversible_command ();
4791 AutomationRangeDrag::aborted (bool)
4793 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4798 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4801 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4802 layer = v->region()->layer ();
4803 initial_y = v->get_canvas_group()->position().y;
4804 initial_playlist = v->region()->playlist ();
4805 initial_position = v->region()->position ();
4806 initial_end = v->region()->position () + v->region()->length ();
4809 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4810 : Drag (e, i->canvas_item ())
4813 , _cumulative_dx (0)
4815 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4816 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4821 PatchChangeDrag::motion (GdkEvent* ev, bool)
4823 framepos_t f = adjusted_current_frame (ev);
4824 boost::shared_ptr<Region> r = _region_view->region ();
4825 f = max (f, r->position ());
4826 f = min (f, r->last_frame ());
4828 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4829 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4830 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4831 _cumulative_dx = dxu;
4835 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4837 if (!movement_occurred) {
4841 boost::shared_ptr<Region> r (_region_view->region ());
4842 framepos_t f = adjusted_current_frame (ev);
4843 f = max (f, r->position ());
4844 f = min (f, r->last_frame ());
4846 _region_view->move_patch_change (
4848 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4853 PatchChangeDrag::aborted (bool)
4855 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4859 PatchChangeDrag::setup_pointer_frame_offset ()
4861 boost::shared_ptr<Region> region = _region_view->region ();
4862 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4865 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4866 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4873 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4875 framepos_t const p = _region_view->region()->position ();
4876 double const y = _region_view->midi_view()->y_position ();
4878 x1 = max ((framepos_t) 0, x1 - p);
4879 x2 = max ((framepos_t) 0, x2 - p);
4880 y1 = max (0.0, y1 - y);
4881 y2 = max (0.0, y2 - y);
4883 _region_view->update_drag_selection (
4884 _editor->sample_to_pixel (x1),
4885 _editor->sample_to_pixel (x2),
4888 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4893 MidiRubberbandSelectDrag::deselect_things ()
4898 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4899 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4902 _vertical_only = true;
4906 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4908 double const y = _region_view->midi_view()->y_position ();
4910 y1 = max (0.0, y1 - y);
4911 y2 = max (0.0, y2 - y);
4913 _region_view->update_vertical_drag_selection (
4916 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4921 MidiVerticalSelectDrag::deselect_things ()
4926 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4927 : RubberbandSelectDrag (e, i)
4933 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4935 if (drag_in_progress) {
4936 /* We just want to select things at the end of the drag, not during it */
4940 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4942 _editor->begin_reversible_command (_("rubberband selection"));
4943 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4944 _editor->commit_reversible_command ();
4948 EditorRubberbandSelectDrag::deselect_things ()
4950 if (!getenv("ARDOUR_SAE")) {
4951 _editor->selection->clear_tracks();
4953 _editor->selection->clear_regions();
4954 _editor->selection->clear_points ();
4955 _editor->selection->clear_lines ();
4958 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4966 NoteCreateDrag::~NoteCreateDrag ()
4972 NoteCreateDrag::grid_frames (framepos_t t) const
4975 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4980 return _region_view->region_beats_to_region_frames (grid_beats);
4984 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4986 Drag::start_grab (event, cursor);
4988 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4990 framepos_t pf = _drags->current_pointer_frame ();
4991 framecnt_t const g = grid_frames (pf);
4993 /* Hack so that we always snap to the note that we are over, instead of snapping
4994 to the next one if we're more than halfway through the one we're over.
4996 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5000 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5002 MidiStreamView* sv = _region_view->midi_stream_view ();
5003 double const x = _editor->sample_to_pixel (_note[0]);
5004 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5006 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5007 _drag_rect->set_outline_all ();
5008 _drag_rect->set_outline_color (0xffffff99);
5009 _drag_rect->set_fill_color (0xffffff66);
5013 NoteCreateDrag::motion (GdkEvent* event, bool)
5015 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5016 double const x = _editor->sample_to_pixel (_note[1]);
5017 if (_note[1] > _note[0]) {
5018 _drag_rect->set_x1 (x);
5020 _drag_rect->set_x0 (x);
5025 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5027 if (!had_movement) {
5031 framepos_t const start = min (_note[0], _note[1]);
5032 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5034 framecnt_t const g = grid_frames (start);
5035 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5037 if (_editor->snap_mode() == SnapNormal && length < g) {
5038 length = g - one_tick;
5041 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5043 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5047 NoteCreateDrag::y_to_region (double y) const
5050 _region_view->get_canvas_group()->canvas_to_item (x, y);
5055 NoteCreateDrag::aborted (bool)
5060 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5065 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5069 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5071 Drag::start_grab (event, cursor);
5075 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5081 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5084 distance = _drags->current_pointer_x() - grab_x();
5085 len = ar->fade_in()->back()->when;
5087 distance = grab_x() - _drags->current_pointer_x();
5088 len = ar->fade_out()->back()->when;
5091 /* how long should it be ? */
5093 new_length = len + _editor->pixel_to_sample (distance);
5095 /* now check with the region that this is legal */
5097 new_length = ar->verify_xfade_bounds (new_length, start);
5100 arv->reset_fade_in_shape_width (ar, new_length);
5102 arv->reset_fade_out_shape_width (ar, new_length);
5107 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5113 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5116 distance = _drags->current_pointer_x() - grab_x();
5117 len = ar->fade_in()->back()->when;
5119 distance = grab_x() - _drags->current_pointer_x();
5120 len = ar->fade_out()->back()->when;
5123 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5125 _editor->begin_reversible_command ("xfade trim");
5126 ar->playlist()->clear_owned_changes ();
5129 ar->set_fade_in_length (new_length);
5131 ar->set_fade_out_length (new_length);
5134 /* Adjusting the xfade may affect other regions in the playlist, so we need
5135 to get undo Commands from the whole playlist rather than just the
5139 vector<Command*> cmds;
5140 ar->playlist()->rdiff (cmds);
5141 _editor->session()->add_commands (cmds);
5142 _editor->commit_reversible_command ();
5147 CrossfadeEdgeDrag::aborted (bool)
5150 arv->redraw_start_xfade ();
5152 arv->redraw_end_xfade ();