2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/scroll_group.h"
48 #include "audio_region_view.h"
49 #include "midi_region_view.h"
50 #include "ardour_ui.h"
51 #include "gui_thread.h"
52 #include "control_point.h"
54 #include "region_gain_line.h"
55 #include "editor_drag.h"
56 #include "audio_time_axis.h"
57 #include "midi_time_axis.h"
58 #include "selection.h"
59 #include "midi_selection.h"
60 #include "automation_time_axis.h"
62 #include "editor_cursors.h"
63 #include "mouse_cursors.h"
64 #include "note_base.h"
65 #include "patch_change.h"
66 #include "verbose_cursor.h"
69 using namespace ARDOUR;
72 using namespace Gtkmm2ext;
73 using namespace Editing;
74 using namespace ArdourCanvas;
76 using Gtkmm2ext::Keyboard;
78 double ControlPointDrag::_zero_gain_fraction = -1.0;
80 DragManager::DragManager (Editor* e)
83 , _current_pointer_frame (0)
87 DragManager::~DragManager ()
92 /** Call abort for each active drag */
98 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
103 if (!_drags.empty ()) {
104 _editor->set_follow_playhead (_old_follow_playhead, false);
113 DragManager::add (Drag* d)
115 d->set_manager (this);
116 _drags.push_back (d);
120 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
122 d->set_manager (this);
123 _drags.push_back (d);
128 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
130 /* Prevent follow playhead during the drag to be nice to the user */
131 _old_follow_playhead = _editor->follow_playhead ();
132 _editor->set_follow_playhead (false);
134 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
136 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
137 (*i)->start_grab (e, c);
141 /** Call end_grab for each active drag.
142 * @return true if any drag reported movement having occurred.
145 DragManager::end_grab (GdkEvent* e)
150 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
151 bool const t = (*i)->end_grab (e);
162 _editor->set_follow_playhead (_old_follow_playhead, false);
168 DragManager::mark_double_click ()
170 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
171 (*i)->set_double_click (true);
176 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
180 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
182 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
183 bool const t = (*i)->motion_handler (e, from_autoscroll);
184 /* run all handlers; return true if at least one of them
185 returns true (indicating that the event has been handled).
197 DragManager::window_motion_handler (GdkEvent* e, bool from_autoscroll)
201 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
203 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
204 bool const t = (*i)->motion_handler (e, from_autoscroll);
215 DragManager::have_item (ArdourCanvas::Item* i) const
217 list<Drag*>::const_iterator j = _drags.begin ();
218 while (j != _drags.end() && (*j)->item () != i) {
222 return j != _drags.end ();
225 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
228 , _pointer_frame_offset (0)
229 , _trackview_only (trackview_only)
230 , _move_threshold_passed (false)
231 , _was_double_click (false)
232 , _raw_grab_frame (0)
234 , _last_pointer_frame (0)
240 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
253 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
255 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
257 if (Keyboard::is_button2_event (&event->button)) {
258 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
259 _y_constrained = true;
260 _x_constrained = false;
262 _y_constrained = false;
263 _x_constrained = true;
266 _x_constrained = false;
267 _y_constrained = false;
270 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
271 setup_pointer_frame_offset ();
272 _grab_frame = adjusted_frame (_raw_grab_frame, event);
273 _last_pointer_frame = _grab_frame;
274 _last_pointer_x = _grab_x;
276 if (_trackview_only) {
277 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
280 _last_pointer_y = _grab_y;
286 /* CAIROCANVAS need a variant here that passes *cursor */
291 if (_editor->session() && _editor->session()->transport_rolling()) {
294 _was_rolling = false;
297 switch (_editor->snap_type()) {
298 case SnapToRegionStart:
299 case SnapToRegionEnd:
300 case SnapToRegionSync:
301 case SnapToRegionBoundary:
302 _editor->build_region_boundary_cache ();
309 /** Call to end a drag `successfully'. Ungrabs item and calls
310 * subclass' finished() method.
312 * @param event GDK event, or 0.
313 * @return true if some movement occurred, otherwise false.
316 Drag::end_grab (GdkEvent* event)
318 _editor->stop_canvas_autoscroll ();
322 finished (event, _move_threshold_passed);
324 _editor->verbose_cursor()->hide ();
326 return _move_threshold_passed;
330 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
334 if (f > _pointer_frame_offset) {
335 pos = f - _pointer_frame_offset;
339 _editor->snap_to_with_modifier (pos, event);
346 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
348 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
352 Drag::current_pointer_y () const
354 if (!_trackview_only) {
355 return _drags->current_pointer_y ();
358 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
362 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
364 /* check to see if we have moved in any way that matters since the last motion event */
365 if (_move_threshold_passed &&
366 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
367 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
371 pair<framecnt_t, int> const threshold = move_threshold ();
373 bool const old_move_threshold_passed = _move_threshold_passed;
375 if (!from_autoscroll && !_move_threshold_passed) {
377 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
378 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
380 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
383 if (active (_editor->mouse_mode) && _move_threshold_passed) {
385 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
386 if (!from_autoscroll) {
387 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
390 if (!_editor->autoscroll_active() || from_autoscroll) {
391 motion (event, _move_threshold_passed != old_move_threshold_passed);
393 _last_pointer_x = _drags->current_pointer_x ();
394 _last_pointer_y = current_pointer_y ();
395 _last_pointer_frame = adjusted_current_frame (event);
404 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
412 aborted (_move_threshold_passed);
414 _editor->stop_canvas_autoscroll ();
415 _editor->verbose_cursor()->hide ();
419 Drag::show_verbose_cursor_time (framepos_t frame)
421 /* We use DragManager::current_pointer_y() here
422 because we need to position the verbose canvas
423 cursor within the overall canvas, regardless
424 of this particular drag's _trackview_only
428 _editor->verbose_cursor()->set_time (
430 _drags->current_pointer_x() + 10,
431 _drags->current_pointer_y() + 10
434 _editor->verbose_cursor()->show ();
438 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
440 _editor->verbose_cursor()->show (xoffset);
442 /* We use DragManager::current_pointer_y() here
443 because we need to position the verbose canvas
444 cursor within the overall canvas, regardless
445 of this particular drag's _trackview_only
449 _editor->verbose_cursor()->set_duration (
451 _drags->current_pointer_x() + 10,
452 _drags->current_pointer_y() + 10
457 Drag::show_verbose_cursor_text (string const & text)
459 _editor->verbose_cursor()->show ();
461 /* We use DragManager::current_pointer_y() here
462 because we need to position the verbose canvas
463 cursor within the overall canvas, regardless
464 of this particular drag's _trackview_only
468 _editor->verbose_cursor()->set (
470 _drags->current_pointer_x() + 10,
471 _drags->current_pointer_y() + 10
475 boost::shared_ptr<Region>
476 Drag::add_midi_region (MidiTimeAxisView* view)
478 if (_editor->session()) {
479 const TempoMap& map (_editor->session()->tempo_map());
480 framecnt_t pos = grab_frame();
481 const Meter& m = map.meter_at (pos);
482 /* not that the frame rate used here can be affected by pull up/down which
485 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
486 return view->add_region (grab_frame(), len, true);
489 return boost::shared_ptr<Region>();
492 struct EditorOrderTimeAxisViewSorter {
493 bool operator() (TimeAxisView* a, TimeAxisView* b) {
494 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
495 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
497 return ra->route()->order_key () < rb->route()->order_key ();
501 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
505 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
507 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
508 as some of the regions we are dragging may be on such tracks.
511 TrackViewList track_views = _editor->track_views;
512 track_views.sort (EditorOrderTimeAxisViewSorter ());
514 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
515 _time_axis_views.push_back (*i);
517 TimeAxisView::Children children_list = (*i)->get_child_list ();
518 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
519 _time_axis_views.push_back (j->get());
523 /* the list of views can be empty at this point if this is a region list-insert drag
526 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
527 _views.push_back (DraggingView (*i, this));
530 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
534 RegionDrag::region_going_away (RegionView* v)
536 list<DraggingView>::iterator i = _views.begin ();
537 while (i != _views.end() && i->view != v) {
541 if (i != _views.end()) {
546 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
547 * or -1 if it is not found.
550 RegionDrag::find_time_axis_view (TimeAxisView* t) const
553 int const N = _time_axis_views.size ();
554 while (i < N && _time_axis_views[i] != t) {
565 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
566 : RegionDrag (e, i, p, v)
569 , _last_pointer_time_axis_view (0)
570 , _last_pointer_layer (0)
572 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
576 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
578 Drag::start_grab (event, cursor);
580 show_verbose_cursor_time (_last_frame_position);
582 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
584 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
585 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
590 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
592 /* compute the amount of pointer motion in frames, and where
593 the region would be if we moved it by that much.
595 *pending_region_position = adjusted_current_frame (event);
597 framepos_t sync_frame;
598 framecnt_t sync_offset;
601 sync_offset = _primary->region()->sync_offset (sync_dir);
603 /* we don't handle a sync point that lies before zero.
605 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
607 sync_frame = *pending_region_position + (sync_dir*sync_offset);
609 _editor->snap_to_with_modifier (sync_frame, event);
611 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
614 *pending_region_position = _last_frame_position;
617 if (*pending_region_position > max_framepos - _primary->region()->length()) {
618 *pending_region_position = _last_frame_position;
623 /* in locked edit mode, reverse the usual meaning of _x_constrained */
624 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
626 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
628 /* x movement since last time (in pixels) */
629 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
631 /* total x movement */
632 framecnt_t total_dx = *pending_region_position;
633 if (regions_came_from_canvas()) {
634 total_dx = total_dx - grab_frame ();
637 /* check that no regions have gone off the start of the session */
638 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
639 if ((i->view->region()->position() + total_dx) < 0) {
641 *pending_region_position = _last_frame_position;
646 _last_frame_position = *pending_region_position;
653 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
655 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
656 int const n = i->time_axis_view + delta_track;
657 if (n < 0 || n >= int (_time_axis_views.size())) {
658 /* off the top or bottom track */
662 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
663 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
664 /* not a track, or the wrong type */
668 double const l = i->layer + delta_layer;
670 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
671 mode to allow the user to place a region below another on layer 0.
673 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
674 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
675 If it has, the layers will be munged later anyway, so it's ok.
681 /* all regions being dragged are ok with this change */
686 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
688 double delta_layer = 0;
689 int delta_time_axis_view = 0;
691 assert (!_views.empty ());
693 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
695 /* Find the TimeAxisView that the pointer is now over */
696 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
697 TimeAxisView* tv = r.first;
699 if (tv && tv->view()) {
700 double layer = r.second;
702 if (first_move && tv->view()->layer_display() == Stacked) {
703 tv->view()->set_layer_display (Expanded);
706 /* Here's the current pointer position in terms of time axis view and layer */
707 int const current_pointer_time_axis_view = find_time_axis_view (tv);
708 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
710 /* Work out the change in y */
712 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
713 delta_layer = current_pointer_layer - _last_pointer_layer;
716 /* Work out the change in x */
717 framepos_t pending_region_position;
718 double const x_delta = compute_x_delta (event, &pending_region_position);
720 /* Verify change in y */
721 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
722 /* this y movement is not allowed, so do no y movement this time */
723 delta_time_axis_view = 0;
727 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
728 /* haven't reached next snap point, and we're not switching
729 trackviews nor layers. nothing to do.
734 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
736 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
738 RegionView* rv = i->view;
740 if (rv->region()->locked() || rv->region()->video_locked()) {
747 /* reparent the regionview into a group above all
751 ArdourCanvas::Group* rvg = rv->get_canvas_group();
752 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
753 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
754 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
755 /* move the item so that it continues to appear at the
756 same location now that its parent has changed.
758 rvg->move (rv_canvas_offset - dmg_canvas_offset);
761 /* If we have moved tracks, we'll fudge the layer delta so that the
762 region gets moved back onto layer 0 on its new track; this avoids
763 confusion when dragging regions from non-zero layers onto different
766 double this_delta_layer = delta_layer;
767 if (delta_time_axis_view != 0) {
768 this_delta_layer = - i->layer;
775 if (i->time_axis_view >= 0) {
776 track_index = i->time_axis_view + delta_time_axis_view;
778 track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
781 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
785 /* The TimeAxisView that this region is now over */
786 TimeAxisView* current_tv = _time_axis_views[track_index];
788 /* Ensure it is moved from stacked -> expanded if appropriate */
789 if (current_tv->view()->layer_display() == Stacked) {
790 current_tv->view()->set_layer_display (Expanded);
793 /* We're only allowed to go -ve in layer on Expanded views */
794 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
795 this_delta_layer = - i->layer;
799 rv->set_height (current_tv->view()->child_height ());
801 /* Update show/hidden status as the region view may have come from a hidden track,
802 or have moved to one.
804 if (current_tv->hidden ()) {
805 rv->get_canvas_group()->hide ();
807 rv->get_canvas_group()->show ();
810 /* Update the DraggingView */
811 i->time_axis_view = track_index;
812 i->layer += this_delta_layer;
815 _editor->mouse_brush_insert_region (rv, pending_region_position);
819 /* Get the y coordinate of the top of the track that this region is now over */
820 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
822 /* And adjust for the layer that it should be on */
823 StreamView* cv = current_tv->view ();
824 switch (cv->layer_display ()) {
828 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
831 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
835 /* need to get the parent of the regionview
836 * canvas group and get its position in
837 * equivalent coordinate space as the trackview
838 * we are now dragging over.
841 /* Now move the region view */
842 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->canvas_origin().y);
846 /* Only move the region into the empty dropzone at the bottom if the pointer
850 if (current_pointer_y() >= 0) {
852 Coord last_track_bottom_edge;
853 if (!_time_axis_views.empty()) {
854 TimeAxisView* last = _time_axis_views.back();
855 last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
857 last_track_bottom_edge = 0;
860 rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y);
861 i->time_axis_view = -1;
865 } /* foreach region */
867 _total_x_delta += x_delta;
869 if (x_delta != 0 && !_brushing) {
870 show_verbose_cursor_time (_last_frame_position);
873 _last_pointer_time_axis_view += delta_time_axis_view;
874 _last_pointer_layer += delta_layer;
878 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
880 if (_copy && first_move) {
882 /* duplicate the regionview(s) and region(s) */
884 list<DraggingView> new_regionviews;
886 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
888 RegionView* rv = i->view;
889 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
890 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
892 const boost::shared_ptr<const Region> original = rv->region();
893 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
894 region_copy->set_position (original->position());
898 boost::shared_ptr<AudioRegion> audioregion_copy
899 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
901 nrv = new AudioRegionView (*arv, audioregion_copy);
903 boost::shared_ptr<MidiRegion> midiregion_copy
904 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
905 nrv = new MidiRegionView (*mrv, midiregion_copy);
910 nrv->get_canvas_group()->show ();
911 new_regionviews.push_back (DraggingView (nrv, this));
913 /* swap _primary to the copy */
915 if (rv == _primary) {
919 /* ..and deselect the one we copied */
921 rv->set_selected (false);
924 if (!new_regionviews.empty()) {
926 /* reflect the fact that we are dragging the copies */
928 _views = new_regionviews;
930 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
934 RegionMotionDrag::motion (event, first_move);
938 RegionMotionDrag::finished (GdkEvent *, bool)
940 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
945 if ((*i)->view()->layer_display() == Expanded) {
946 (*i)->view()->set_layer_display (Stacked);
952 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
954 RegionMotionDrag::finished (ev, movement_occurred);
956 if (!movement_occurred) {
960 if (was_double_click() && !_views.empty()) {
961 DraggingView dv = _views.front();
962 dv.view->show_region_editor ();
969 /* reverse this here so that we have the correct logic to finalize
973 if (Config->get_edit_mode() == Lock) {
974 _x_constrained = !_x_constrained;
977 assert (!_views.empty ());
979 /* We might have hidden region views so that they weren't visible during the drag
980 (when they have been reparented). Now everything can be shown again, as region
981 views are back in their track parent groups.
983 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
984 i->view->get_canvas_group()->show ();
987 bool const changed_position = (_last_frame_position != _primary->region()->position());
988 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
989 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1009 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1013 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region)
1015 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1020 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1021 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1022 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1023 return _editor->axis_view_from_route (audio_tracks.front());
1025 ChanCount one_midi_port (DataType::MIDI, 1);
1026 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1027 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1028 return _editor->axis_view_from_route (midi_tracks.front());
1031 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1037 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1039 RegionSelection new_views;
1040 PlaylistSet modified_playlists;
1041 RouteTimeAxisView* new_time_axis_view = 0;
1044 /* all changes were made during motion event handlers */
1046 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1050 _editor->commit_reversible_command ();
1054 if (_x_constrained) {
1055 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1057 _editor->begin_reversible_command (Operations::region_copy);
1060 /* insert the regions into their new playlists */
1061 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1063 RouteTimeAxisView* dest_rtv = 0;
1065 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1071 if (changed_position && !_x_constrained) {
1072 where = i->view->region()->position() - drag_delta;
1074 where = i->view->region()->position();
1077 if (i->time_axis_view < 0) {
1078 if (!new_time_axis_view) {
1079 new_time_axis_view = create_destination_time_axis (i->view->region());
1081 dest_rtv = new_time_axis_view;
1083 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1086 if (dest_rtv != 0) {
1087 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1088 if (new_view != 0) {
1089 new_views.push_back (new_view);
1093 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1094 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1097 list<DraggingView>::const_iterator next = i;
1103 /* If we've created new regions either by copying or moving
1104 to a new track, we want to replace the old selection with the new ones
1107 if (new_views.size() > 0) {
1108 _editor->selection->set (new_views);
1111 /* write commands for the accumulated diffs for all our modified playlists */
1112 add_stateful_diff_commands_for_playlists (modified_playlists);
1114 _editor->commit_reversible_command ();
1118 RegionMoveDrag::finished_no_copy (
1119 bool const changed_position,
1120 bool const changed_tracks,
1121 framecnt_t const drag_delta
1124 RegionSelection new_views;
1125 PlaylistSet modified_playlists;
1126 PlaylistSet frozen_playlists;
1127 set<RouteTimeAxisView*> views_to_update;
1128 RouteTimeAxisView* new_time_axis_view = 0;
1131 /* all changes were made during motion event handlers */
1132 _editor->commit_reversible_command ();
1136 if (_x_constrained) {
1137 _editor->begin_reversible_command (_("fixed time region drag"));
1139 _editor->begin_reversible_command (Operations::region_drag);
1142 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1144 RegionView* rv = i->view;
1145 RouteTimeAxisView* dest_rtv = 0;
1147 if (rv->region()->locked() || rv->region()->video_locked()) {
1152 if (i->time_axis_view < 0) {
1153 if (!new_time_axis_view) {
1154 new_time_axis_view = create_destination_time_axis (rv->region());
1156 dest_rtv = new_time_axis_view;
1158 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1163 double const dest_layer = i->layer;
1165 views_to_update.insert (dest_rtv);
1169 if (changed_position && !_x_constrained) {
1170 where = rv->region()->position() - drag_delta;
1172 where = rv->region()->position();
1175 if (changed_tracks) {
1177 /* insert into new playlist */
1179 RegionView* new_view = insert_region_into_playlist (
1180 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1183 if (new_view == 0) {
1188 new_views.push_back (new_view);
1190 /* remove from old playlist */
1192 /* the region that used to be in the old playlist is not
1193 moved to the new one - we use a copy of it. as a result,
1194 any existing editor for the region should no longer be
1197 rv->hide_region_editor();
1200 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1204 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1206 /* this movement may result in a crossfade being modified, or a layering change,
1207 so we need to get undo data from the playlist as well as the region.
1210 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1212 playlist->clear_changes ();
1215 rv->region()->clear_changes ();
1218 motion on the same track. plonk the previously reparented region
1219 back to its original canvas group (its streamview).
1220 No need to do anything for copies as they are fake regions which will be deleted.
1223 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1224 rv->get_canvas_group()->set_y_position (i->initial_y);
1227 /* just change the model */
1228 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1229 playlist->set_layer (rv->region(), dest_layer);
1232 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1234 r = frozen_playlists.insert (playlist);
1237 playlist->freeze ();
1240 rv->region()->set_position (where);
1242 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1245 if (changed_tracks) {
1247 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1248 was selected in all of them, then removing it from a playlist will have removed all
1249 trace of it from _views (i.e. there were N regions selected, we removed 1,
1250 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1251 corresponding regionview, and _views is now empty).
1253 This could have invalidated any and all iterators into _views.
1255 The heuristic we use here is: if the region selection is empty, break out of the loop
1256 here. if the region selection is not empty, then restart the loop because we know that
1257 we must have removed at least the region(view) we've just been working on as well as any
1258 that we processed on previous iterations.
1260 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1261 we can just iterate.
1265 if (_views.empty()) {
1276 /* If we've created new regions either by copying or moving
1277 to a new track, we want to replace the old selection with the new ones
1280 if (new_views.size() > 0) {
1281 _editor->selection->set (new_views);
1284 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1288 /* write commands for the accumulated diffs for all our modified playlists */
1289 add_stateful_diff_commands_for_playlists (modified_playlists);
1291 _editor->commit_reversible_command ();
1293 /* We have futzed with the layering of canvas items on our streamviews.
1294 If any region changed layer, this will have resulted in the stream
1295 views being asked to set up their region views, and all will be well.
1296 If not, we might now have badly-ordered region views. Ask the StreamViews
1297 involved to sort themselves out, just in case.
1300 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1301 (*i)->view()->playlist_layered ((*i)->track ());
1305 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1306 * @param region Region to remove.
1307 * @param playlist playlist To remove from.
1308 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1309 * that clear_changes () is only called once per playlist.
1312 RegionMoveDrag::remove_region_from_playlist (
1313 boost::shared_ptr<Region> region,
1314 boost::shared_ptr<Playlist> playlist,
1315 PlaylistSet& modified_playlists
1318 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1321 playlist->clear_changes ();
1324 playlist->remove_region (region);
1328 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1329 * clearing the playlist's diff history first if necessary.
1330 * @param region Region to insert.
1331 * @param dest_rtv Destination RouteTimeAxisView.
1332 * @param dest_layer Destination layer.
1333 * @param where Destination position.
1334 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1335 * that clear_changes () is only called once per playlist.
1336 * @return New RegionView, or 0 if no insert was performed.
1339 RegionMoveDrag::insert_region_into_playlist (
1340 boost::shared_ptr<Region> region,
1341 RouteTimeAxisView* dest_rtv,
1344 PlaylistSet& modified_playlists
1347 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1348 if (!dest_playlist) {
1352 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1353 _new_region_view = 0;
1354 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1356 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1357 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1359 dest_playlist->clear_changes ();
1362 dest_playlist->add_region (region, where);
1364 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1365 dest_playlist->set_layer (region, dest_layer);
1370 assert (_new_region_view);
1372 return _new_region_view;
1376 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1378 _new_region_view = rv;
1382 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1384 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1385 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1387 _editor->session()->add_command (c);
1396 RegionMoveDrag::aborted (bool movement_occurred)
1400 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1407 RegionMotionDrag::aborted (movement_occurred);
1412 RegionMotionDrag::aborted (bool)
1414 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1416 StreamView* sview = (*i)->view();
1419 if (sview->layer_display() == Expanded) {
1420 sview->set_layer_display (Stacked);
1425 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1426 RegionView* rv = i->view;
1427 TimeAxisView* tv = &(rv->get_time_axis_view ());
1428 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1430 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1431 rv->get_canvas_group()->set_y_position (0);
1433 rv->move (-_total_x_delta, 0);
1434 rv->set_height (rtv->view()->child_height ());
1438 /** @param b true to brush, otherwise false.
1439 * @param c true to make copies of the regions being moved, otherwise false.
1441 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1442 : RegionMotionDrag (e, i, p, v, b),
1445 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1448 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1449 if (rtv && rtv->is_track()) {
1450 speed = rtv->track()->speed ();
1453 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1457 RegionMoveDrag::setup_pointer_frame_offset ()
1459 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1462 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1463 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1465 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1467 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1468 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1470 _primary = v->view()->create_region_view (r, false, false);
1472 _primary->get_canvas_group()->show ();
1473 _primary->set_position (pos, 0);
1474 _views.push_back (DraggingView (_primary, this));
1476 _last_frame_position = pos;
1478 _item = _primary->get_canvas_group ();
1482 RegionInsertDrag::finished (GdkEvent *, bool)
1484 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1486 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1487 _primary->get_canvas_group()->set_y_position (0);
1489 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1491 _editor->begin_reversible_command (Operations::insert_region);
1492 playlist->clear_changes ();
1493 playlist->add_region (_primary->region (), _last_frame_position);
1494 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1495 _editor->commit_reversible_command ();
1503 RegionInsertDrag::aborted (bool)
1510 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1511 : RegionMoveDrag (e, i, p, v, false, false)
1513 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1516 struct RegionSelectionByPosition {
1517 bool operator() (RegionView*a, RegionView* b) {
1518 return a->region()->position () < b->region()->position();
1523 RegionSpliceDrag::motion (GdkEvent* event, bool)
1525 /* Which trackview is this ? */
1527 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1528 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1530 /* The region motion is only processed if the pointer is over
1534 if (!tv || !tv->is_track()) {
1535 /* To make sure we hide the verbose canvas cursor when the mouse is
1536 not held over and audiotrack.
1538 _editor->verbose_cursor()->hide ();
1544 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1550 RegionSelection copy (_editor->selection->regions);
1552 RegionSelectionByPosition cmp;
1555 framepos_t const pf = adjusted_current_frame (event);
1557 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1559 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1565 boost::shared_ptr<Playlist> playlist;
1567 if ((playlist = atv->playlist()) == 0) {
1571 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1576 if (pf < (*i)->region()->last_frame() + 1) {
1580 if (pf > (*i)->region()->first_frame()) {
1586 playlist->shuffle ((*i)->region(), dir);
1591 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1593 RegionMoveDrag::finished (event, movement_occurred);
1597 RegionSpliceDrag::aborted (bool)
1602 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1604 _view (dynamic_cast<MidiTimeAxisView*> (v))
1606 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1612 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1615 _region = add_midi_region (_view);
1616 _view->playlist()->freeze ();
1619 framepos_t const f = adjusted_current_frame (event);
1620 if (f < grab_frame()) {
1621 _region->set_position (f);
1624 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1625 so that if this region is duplicated, its duplicate starts on
1626 a snap point rather than 1 frame after a snap point. Otherwise things get
1627 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1628 place snapped notes at the start of the region.
1631 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1632 _region->set_length (len < 1 ? 1 : len);
1638 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1640 if (!movement_occurred) {
1641 add_midi_region (_view);
1643 _view->playlist()->thaw ();
1648 RegionCreateDrag::aborted (bool)
1651 _view->playlist()->thaw ();
1657 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1661 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1665 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1667 Gdk::Cursor* cursor;
1668 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1670 float x_fraction = cnote->mouse_x_fraction ();
1672 if (x_fraction > 0.0 && x_fraction < 0.25) {
1673 cursor = _editor->cursors()->left_side_trim;
1675 cursor = _editor->cursors()->right_side_trim;
1678 Drag::start_grab (event, cursor);
1680 region = &cnote->region_view();
1682 double const region_start = region->get_position_pixels();
1683 double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1685 if (grab_x() <= middle_point) {
1686 cursor = _editor->cursors()->left_side_trim;
1689 cursor = _editor->cursors()->right_side_trim;
1695 if (event->motion.state & Keyboard::PrimaryModifier) {
1701 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1703 if (ms.size() > 1) {
1704 /* has to be relative, may make no sense otherwise */
1708 /* select this note; if it is already selected, preserve the existing selection,
1709 otherwise make this note the only one selected.
1711 region->note_selected (cnote, cnote->selected ());
1713 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1714 MidiRegionSelection::iterator next;
1717 (*r)->begin_resizing (at_front);
1723 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1725 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1726 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1727 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1729 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1734 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1736 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1737 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1738 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1740 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1745 NoteResizeDrag::aborted (bool)
1747 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1748 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1749 (*r)->abort_resizing ();
1753 AVDraggingView::AVDraggingView (RegionView* v)
1756 initial_position = v->region()->position ();
1759 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1762 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1765 TrackViewList empty;
1767 _editor->get_regions_after(rs, (framepos_t) 0, empty);
1768 std::list<RegionView*> views = rs.by_layer();
1770 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1771 RegionView* rv = (*i);
1772 if (!rv->region()->video_locked()) {
1775 _views.push_back (AVDraggingView (rv));
1780 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1782 Drag::start_grab (event);
1783 if (_editor->session() == 0) {
1787 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1788 _max_backwards_drag = (
1789 ARDOUR_UI::instance()->video_timeline->get_duration()
1790 + ARDOUR_UI::instance()->video_timeline->get_offset()
1791 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1794 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1795 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1796 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1799 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1802 Timecode::Time timecode;
1803 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1804 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);
1805 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1806 _editor->verbose_cursor()->show ();
1810 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1812 if (_editor->session() == 0) {
1815 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1819 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1820 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
1822 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1823 dt = - _max_backwards_drag;
1826 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1827 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1829 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1830 RegionView* rv = i->view;
1831 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1834 rv->region()->clear_changes ();
1835 rv->region()->suspend_property_changes();
1837 rv->region()->set_position(i->initial_position + dt);
1838 rv->region_changed(ARDOUR::Properties::position);
1841 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1842 Timecode::Time timecode;
1843 Timecode::Time timediff;
1845 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1846 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1847 snprintf (buf, sizeof (buf),
1848 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1849 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1850 , _("Video Start:"),
1851 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1853 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1855 _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1856 _editor->verbose_cursor()->show ();
1860 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1862 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1866 if (!movement_occurred || ! _editor->session()) {
1870 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1872 _editor->begin_reversible_command (_("Move Video"));
1874 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1875 ARDOUR_UI::instance()->video_timeline->save_undo();
1876 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1877 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1879 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1880 i->view->drag_end();
1881 i->view->region()->resume_property_changes ();
1883 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1886 _editor->session()->maybe_update_session_range(
1887 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1888 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1892 _editor->commit_reversible_command ();
1896 VideoTimeLineDrag::aborted (bool)
1898 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1901 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1902 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1904 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1905 i->view->region()->resume_property_changes ();
1906 i->view->region()->set_position(i->initial_position);
1910 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1911 : RegionDrag (e, i, p, v)
1912 , _preserve_fade_anchor (preserve_fade_anchor)
1913 , _jump_position_when_done (false)
1915 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1919 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1922 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1923 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1925 if (tv && tv->is_track()) {
1926 speed = tv->track()->speed();
1929 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1930 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1931 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1933 framepos_t const pf = adjusted_current_frame (event);
1935 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1936 /* Move the contents of the region around without changing the region bounds */
1937 _operation = ContentsTrim;
1938 Drag::start_grab (event, _editor->cursors()->trimmer);
1940 /* These will get overridden for a point trim.*/
1941 if (pf < (region_start + region_length/2)) {
1942 /* closer to front */
1943 _operation = StartTrim;
1944 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1947 _operation = EndTrim;
1948 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1952 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1953 _jump_position_when_done = true;
1956 switch (_operation) {
1958 show_verbose_cursor_time (region_start);
1959 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1960 i->view->trim_front_starting ();
1964 show_verbose_cursor_time (region_end);
1967 show_verbose_cursor_time (pf);
1971 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1972 i->view->region()->suspend_property_changes ();
1977 TrimDrag::motion (GdkEvent* event, bool first_move)
1979 RegionView* rv = _primary;
1982 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1983 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1984 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1985 frameoffset_t frame_delta = 0;
1987 if (tv && tv->is_track()) {
1988 speed = tv->track()->speed();
1991 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1997 switch (_operation) {
1999 trim_type = "Region start trim";
2002 trim_type = "Region end trim";
2005 trim_type = "Region content trim";
2012 _editor->begin_reversible_command (trim_type);
2014 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2015 RegionView* rv = i->view;
2016 rv->enable_display (false);
2017 rv->region()->playlist()->clear_owned_changes ();
2019 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2022 arv->temporarily_hide_envelope ();
2026 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2027 insert_result = _editor->motion_frozen_playlists.insert (pl);
2029 if (insert_result.second) {
2035 bool non_overlap_trim = false;
2037 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2038 non_overlap_trim = true;
2041 /* contstrain trim to fade length */
2042 if (_preserve_fade_anchor) {
2043 switch (_operation) {
2045 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2046 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2048 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2049 if (ar->locked()) continue;
2050 framecnt_t len = ar->fade_in()->back()->when;
2051 if (len < dt) dt = min(dt, len);
2055 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2056 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2058 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2059 if (ar->locked()) continue;
2060 framecnt_t len = ar->fade_out()->back()->when;
2061 if (len < -dt) dt = max(dt, -len);
2070 switch (_operation) {
2072 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2073 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2074 if (changed && _preserve_fade_anchor) {
2075 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2077 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2078 framecnt_t len = ar->fade_in()->back()->when;
2079 framecnt_t diff = ar->first_frame() - i->initial_position;
2080 framepos_t new_length = len - diff;
2081 i->anchored_fade_length = min (ar->length(), new_length);
2082 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2083 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2090 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2091 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2092 if (changed && _preserve_fade_anchor) {
2093 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2095 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2096 framecnt_t len = ar->fade_out()->back()->when;
2097 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2098 framepos_t new_length = len + diff;
2099 i->anchored_fade_length = min (ar->length(), new_length);
2100 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2101 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2109 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2111 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2112 i->view->move_contents (frame_delta);
2118 switch (_operation) {
2120 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2123 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2126 // show_verbose_cursor_time (frame_delta);
2133 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2135 if (movement_occurred) {
2136 motion (event, false);
2138 if (_operation == StartTrim) {
2139 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2141 /* This must happen before the region's StatefulDiffCommand is created, as it may
2142 `correct' (ahem) the region's _start from being negative to being zero. It
2143 needs to be zero in the undo record.
2145 i->view->trim_front_ending ();
2147 if (_preserve_fade_anchor) {
2148 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2150 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2151 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2152 ar->set_fade_in_length(i->anchored_fade_length);
2153 ar->set_fade_in_active(true);
2156 if (_jump_position_when_done) {
2157 i->view->region()->set_position (i->initial_position);
2160 } else if (_operation == EndTrim) {
2161 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2162 if (_preserve_fade_anchor) {
2163 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2165 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2166 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2167 ar->set_fade_out_length(i->anchored_fade_length);
2168 ar->set_fade_out_active(true);
2171 if (_jump_position_when_done) {
2172 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2177 if (!_views.empty()) {
2178 if (_operation == StartTrim) {
2179 _editor->maybe_locate_with_edit_preroll(
2180 _views.begin()->view->region()->position());
2182 if (_operation == EndTrim) {
2183 _editor->maybe_locate_with_edit_preroll(
2184 _views.begin()->view->region()->position() +
2185 _views.begin()->view->region()->length());
2189 if (!_editor->selection->selected (_primary)) {
2190 _primary->thaw_after_trim ();
2193 set<boost::shared_ptr<Playlist> > diffed_playlists;
2195 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2196 i->view->thaw_after_trim ();
2197 i->view->enable_display (true);
2199 /* Trimming one region may affect others on the playlist, so we need
2200 to get undo Commands from the whole playlist rather than just the
2201 region. Use diffed_playlists to make sure we don't diff a given
2202 playlist more than once.
2204 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2205 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2206 vector<Command*> cmds;
2208 _editor->session()->add_commands (cmds);
2209 diffed_playlists.insert (p);
2214 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2218 _editor->motion_frozen_playlists.clear ();
2219 _editor->commit_reversible_command();
2222 /* no mouse movement */
2223 _editor->point_trim (event, adjusted_current_frame (event));
2226 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2227 if (_operation == StartTrim) {
2228 i->view->trim_front_ending ();
2231 i->view->region()->resume_property_changes ();
2236 TrimDrag::aborted (bool movement_occurred)
2238 /* Our motion method is changing model state, so use the Undo system
2239 to cancel. Perhaps not ideal, as this will leave an Undo point
2240 behind which may be slightly odd from the user's point of view.
2245 if (movement_occurred) {
2249 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2250 i->view->region()->resume_property_changes ();
2255 TrimDrag::setup_pointer_frame_offset ()
2257 list<DraggingView>::iterator i = _views.begin ();
2258 while (i != _views.end() && i->view != _primary) {
2262 if (i == _views.end()) {
2266 switch (_operation) {
2268 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2271 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2278 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2282 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2283 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2288 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2290 Drag::start_grab (event, cursor);
2291 show_verbose_cursor_time (adjusted_current_frame(event));
2295 MeterMarkerDrag::setup_pointer_frame_offset ()
2297 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2301 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2303 if (!_marker->meter().movable()) {
2309 // create a dummy marker for visual representation of moving the
2310 // section, because whether its a copy or not, we're going to
2311 // leave or lose the original marker (leave if its a copy; lose if its
2312 // not, because we'll remove it from the map).
2314 MeterSection section (_marker->meter());
2316 if (!section.movable()) {
2321 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2323 _marker = new MeterMarker (
2325 *_editor->meter_group,
2326 ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2328 *new MeterSection (_marker->meter())
2331 /* use the new marker for the grab */
2332 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2335 TempoMap& map (_editor->session()->tempo_map());
2336 /* get current state */
2337 before_state = &map.get_state();
2338 /* remove the section while we drag it */
2339 map.remove_meter (section, true);
2343 framepos_t const pf = adjusted_current_frame (event);
2344 _marker->set_position (pf);
2345 show_verbose_cursor_time (pf);
2349 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2351 if (!movement_occurred) {
2352 if (was_double_click()) {
2353 _editor->edit_meter_marker (*_marker);
2358 if (!_marker->meter().movable()) {
2362 motion (event, false);
2364 Timecode::BBT_Time when;
2366 TempoMap& map (_editor->session()->tempo_map());
2367 map.bbt_time (last_pointer_frame(), when);
2369 if (_copy == true) {
2370 _editor->begin_reversible_command (_("copy meter mark"));
2371 XMLNode &before = map.get_state();
2372 map.add_meter (_marker->meter(), when);
2373 XMLNode &after = map.get_state();
2374 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2375 _editor->commit_reversible_command ();
2378 _editor->begin_reversible_command (_("move meter mark"));
2380 /* we removed it before, so add it back now */
2382 map.add_meter (_marker->meter(), when);
2383 XMLNode &after = map.get_state();
2384 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2385 _editor->commit_reversible_command ();
2388 // delete the dummy marker we used for visual representation while moving.
2389 // a new visual marker will show up automatically.
2394 MeterMarkerDrag::aborted (bool moved)
2396 _marker->set_position (_marker->meter().frame ());
2399 TempoMap& map (_editor->session()->tempo_map());
2400 /* we removed it before, so add it back now */
2401 map.add_meter (_marker->meter(), _marker->meter().frame());
2402 // delete the dummy marker we used for visual representation while moving.
2403 // a new visual marker will show up automatically.
2408 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2412 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2414 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2419 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2421 Drag::start_grab (event, cursor);
2422 show_verbose_cursor_time (adjusted_current_frame (event));
2426 TempoMarkerDrag::setup_pointer_frame_offset ()
2428 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2432 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2434 if (!_marker->tempo().movable()) {
2440 // create a dummy marker for visual representation of moving the
2441 // section, because whether its a copy or not, we're going to
2442 // leave or lose the original marker (leave if its a copy; lose if its
2443 // not, because we'll remove it from the map).
2445 // create a dummy marker for visual representation of moving the copy.
2446 // The actual copying is not done before we reach the finish callback.
2449 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2451 TempoSection section (_marker->tempo());
2453 _marker = new TempoMarker (
2455 *_editor->tempo_group,
2456 ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2458 *new TempoSection (_marker->tempo())
2461 /* use the new marker for the grab */
2462 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2465 TempoMap& map (_editor->session()->tempo_map());
2466 /* get current state */
2467 before_state = &map.get_state();
2468 /* remove the section while we drag it */
2469 map.remove_tempo (section, true);
2473 framepos_t const pf = adjusted_current_frame (event);
2474 _marker->set_position (pf);
2475 show_verbose_cursor_time (pf);
2479 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2481 if (!movement_occurred) {
2482 if (was_double_click()) {
2483 _editor->edit_tempo_marker (*_marker);
2488 if (!_marker->tempo().movable()) {
2492 motion (event, false);
2494 TempoMap& map (_editor->session()->tempo_map());
2495 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2496 Timecode::BBT_Time when;
2498 map.bbt_time (beat_time, when);
2500 if (_copy == true) {
2501 _editor->begin_reversible_command (_("copy tempo mark"));
2502 XMLNode &before = map.get_state();
2503 map.add_tempo (_marker->tempo(), when);
2504 XMLNode &after = map.get_state();
2505 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2506 _editor->commit_reversible_command ();
2509 _editor->begin_reversible_command (_("move tempo mark"));
2510 /* we removed it before, so add it back now */
2511 map.add_tempo (_marker->tempo(), when);
2512 XMLNode &after = map.get_state();
2513 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2514 _editor->commit_reversible_command ();
2517 // delete the dummy marker we used for visual representation while moving.
2518 // a new visual marker will show up automatically.
2523 TempoMarkerDrag::aborted (bool moved)
2525 _marker->set_position (_marker->tempo().frame());
2527 TempoMap& map (_editor->session()->tempo_map());
2528 /* we removed it before, so add it back now */
2529 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2530 // delete the dummy marker we used for visual representation while moving.
2531 // a new visual marker will show up automatically.
2536 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2537 : Drag (e, &c.track_canvas_item(), false)
2541 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2544 /** Do all the things we do when dragging the playhead to make it look as though
2545 * we have located, without actually doing the locate (because that would cause
2546 * the diskstream buffers to be refilled, which is too slow).
2549 CursorDrag::fake_locate (framepos_t t)
2551 _editor->playhead_cursor->set_position (t);
2553 Session* s = _editor->session ();
2554 if (s->timecode_transmission_suspended ()) {
2555 framepos_t const f = _editor->playhead_cursor->current_frame ();
2556 /* This is asynchronous so it will be sent "now"
2558 s->send_mmc_locate (f);
2559 /* These are synchronous and will be sent during the next
2562 s->queue_full_time_code ();
2563 s->queue_song_position_pointer ();
2566 show_verbose_cursor_time (t);
2567 _editor->UpdateAllTransportClocks (t);
2571 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2573 Drag::start_grab (event, c);
2575 _grab_zoom = _editor->samples_per_pixel;
2577 framepos_t where = _editor->canvas_event_sample (event);
2579 _editor->snap_to_with_modifier (where, event);
2581 _editor->_dragging_playhead = true;
2583 Session* s = _editor->session ();
2585 /* grab the track canvas item as well */
2587 _cursor.track_canvas_item().grab();
2590 if (_was_rolling && _stop) {
2594 if (s->is_auditioning()) {
2595 s->cancel_audition ();
2599 if (AudioEngine::instance()->connected()) {
2601 /* do this only if we're the engine is connected
2602 * because otherwise this request will never be
2603 * serviced and we'll busy wait forever. likewise,
2604 * notice if we are disconnected while waiting for the
2605 * request to be serviced.
2608 s->request_suspend_timecode_transmission ();
2609 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2610 /* twiddle our thumbs */
2615 fake_locate (where);
2619 CursorDrag::motion (GdkEvent* event, bool)
2621 framepos_t const adjusted_frame = adjusted_current_frame (event);
2622 if (adjusted_frame != last_pointer_frame()) {
2623 fake_locate (adjusted_frame);
2628 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2630 _editor->_dragging_playhead = false;
2632 _cursor.track_canvas_item().ungrab();
2634 if (!movement_occurred && _stop) {
2638 motion (event, false);
2640 Session* s = _editor->session ();
2642 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2643 _editor->_pending_locate_request = true;
2644 s->request_resume_timecode_transmission ();
2649 CursorDrag::aborted (bool)
2651 _cursor.track_canvas_item().ungrab();
2653 if (_editor->_dragging_playhead) {
2654 _editor->session()->request_resume_timecode_transmission ();
2655 _editor->_dragging_playhead = false;
2658 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2661 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2662 : RegionDrag (e, i, p, v)
2664 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2668 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2670 Drag::start_grab (event, cursor);
2672 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2673 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2675 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2679 FadeInDrag::setup_pointer_frame_offset ()
2681 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2682 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2683 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2687 FadeInDrag::motion (GdkEvent* event, bool)
2689 framecnt_t fade_length;
2691 framepos_t const pos = adjusted_current_frame (event);
2693 boost::shared_ptr<Region> region = _primary->region ();
2695 if (pos < (region->position() + 64)) {
2696 fade_length = 64; // this should be a minimum defined somewhere
2697 } else if (pos > region->last_frame()) {
2698 fade_length = region->length();
2700 fade_length = pos - region->position();
2703 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2705 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2711 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2714 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2718 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2720 if (!movement_occurred) {
2724 framecnt_t fade_length;
2726 framepos_t const pos = adjusted_current_frame (event);
2728 boost::shared_ptr<Region> region = _primary->region ();
2730 if (pos < (region->position() + 64)) {
2731 fade_length = 64; // this should be a minimum defined somewhere
2732 } else if (pos > region->last_frame()) {
2733 fade_length = region->length();
2735 fade_length = pos - region->position();
2738 _editor->begin_reversible_command (_("change fade in length"));
2740 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2742 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2748 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2749 XMLNode &before = alist->get_state();
2751 tmp->audio_region()->set_fade_in_length (fade_length);
2752 tmp->audio_region()->set_fade_in_active (true);
2754 XMLNode &after = alist->get_state();
2755 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2758 _editor->commit_reversible_command ();
2762 FadeInDrag::aborted (bool)
2764 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2765 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2771 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2775 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2776 : RegionDrag (e, i, p, v)
2778 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2782 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2784 Drag::start_grab (event, cursor);
2786 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2787 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2789 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2793 FadeOutDrag::setup_pointer_frame_offset ()
2795 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2796 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2797 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2801 FadeOutDrag::motion (GdkEvent* event, bool)
2803 framecnt_t fade_length;
2805 framepos_t const pos = adjusted_current_frame (event);
2807 boost::shared_ptr<Region> region = _primary->region ();
2809 if (pos > (region->last_frame() - 64)) {
2810 fade_length = 64; // this should really be a minimum fade defined somewhere
2812 else if (pos < region->position()) {
2813 fade_length = region->length();
2816 fade_length = region->last_frame() - pos;
2819 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2821 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2827 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2830 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2834 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2836 if (!movement_occurred) {
2840 framecnt_t fade_length;
2842 framepos_t const pos = adjusted_current_frame (event);
2844 boost::shared_ptr<Region> region = _primary->region ();
2846 if (pos > (region->last_frame() - 64)) {
2847 fade_length = 64; // this should really be a minimum fade defined somewhere
2849 else if (pos < region->position()) {
2850 fade_length = region->length();
2853 fade_length = region->last_frame() - pos;
2856 _editor->begin_reversible_command (_("change fade out length"));
2858 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2860 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2866 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2867 XMLNode &before = alist->get_state();
2869 tmp->audio_region()->set_fade_out_length (fade_length);
2870 tmp->audio_region()->set_fade_out_active (true);
2872 XMLNode &after = alist->get_state();
2873 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2876 _editor->commit_reversible_command ();
2880 FadeOutDrag::aborted (bool)
2882 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2883 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2889 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2893 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2896 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2898 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2901 _points.push_back (ArdourCanvas::Duple (0, 0));
2902 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2905 MarkerDrag::~MarkerDrag ()
2907 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2912 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2914 location = new Location (*l);
2915 markers.push_back (m);
2920 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2922 Drag::start_grab (event, cursor);
2926 Location *location = _editor->find_location_from_marker (_marker, is_start);
2927 _editor->_dragging_edit_point = true;
2929 update_item (location);
2931 // _drag_line->show();
2932 // _line->raise_to_top();
2935 show_verbose_cursor_time (location->start());
2937 show_verbose_cursor_time (location->end());
2940 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2943 case Selection::Toggle:
2944 /* we toggle on the button release */
2946 case Selection::Set:
2947 if (!_editor->selection->selected (_marker)) {
2948 _editor->selection->set (_marker);
2951 case Selection::Extend:
2953 Locations::LocationList ll;
2954 list<Marker*> to_add;
2956 _editor->selection->markers.range (s, e);
2957 s = min (_marker->position(), s);
2958 e = max (_marker->position(), e);
2961 if (e < max_framepos) {
2964 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2965 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2966 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2969 to_add.push_back (lm->start);
2972 to_add.push_back (lm->end);
2976 if (!to_add.empty()) {
2977 _editor->selection->add (to_add);
2981 case Selection::Add:
2982 _editor->selection->add (_marker);
2986 /* Set up copies for us to manipulate during the drag
2989 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2991 Location* l = _editor->find_location_from_marker (*i, is_start);
2998 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3000 /* range: check that the other end of the range isn't
3003 CopiedLocationInfo::iterator x;
3004 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3005 if (*(*x).location == *l) {
3009 if (x == _copied_locations.end()) {
3010 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3012 (*x).markers.push_back (*i);
3013 (*x).move_both = true;
3021 MarkerDrag::setup_pointer_frame_offset ()
3024 Location *location = _editor->find_location_from_marker (_marker, is_start);
3025 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3029 MarkerDrag::motion (GdkEvent* event, bool)
3031 framecnt_t f_delta = 0;
3033 bool move_both = false;
3034 Location *real_location;
3035 Location *copy_location = 0;
3037 framepos_t const newframe = adjusted_current_frame (event);
3038 framepos_t next = newframe;
3040 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3044 CopiedLocationInfo::iterator x;
3046 /* find the marker we're dragging, and compute the delta */
3048 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3050 copy_location = (*x).location;
3052 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3054 /* this marker is represented by this
3055 * CopiedLocationMarkerInfo
3058 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3063 if (real_location->is_mark()) {
3064 f_delta = newframe - copy_location->start();
3068 switch (_marker->type()) {
3069 case Marker::SessionStart:
3070 case Marker::RangeStart:
3071 case Marker::LoopStart:
3072 case Marker::PunchIn:
3073 f_delta = newframe - copy_location->start();
3076 case Marker::SessionEnd:
3077 case Marker::RangeEnd:
3078 case Marker::LoopEnd:
3079 case Marker::PunchOut:
3080 f_delta = newframe - copy_location->end();
3083 /* what kind of marker is this ? */
3092 if (x == _copied_locations.end()) {
3093 /* hmm, impossible - we didn't find the dragged marker */
3097 /* now move them all */
3099 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3101 copy_location = x->location;
3103 /* call this to find out if its the start or end */
3105 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3109 if (real_location->locked()) {
3113 if (copy_location->is_mark()) {
3117 copy_location->set_start (copy_location->start() + f_delta);
3121 framepos_t new_start = copy_location->start() + f_delta;
3122 framepos_t new_end = copy_location->end() + f_delta;
3124 if (is_start) { // start-of-range marker
3126 if (move_both || (*x).move_both) {
3127 copy_location->set_start (new_start);
3128 copy_location->set_end (new_end);
3129 } else if (new_start < copy_location->end()) {
3130 copy_location->set_start (new_start);
3131 } else if (newframe > 0) {
3132 _editor->snap_to (next, 1, true);
3133 copy_location->set_end (next);
3134 copy_location->set_start (newframe);
3137 } else { // end marker
3139 if (move_both || (*x).move_both) {
3140 copy_location->set_end (new_end);
3141 copy_location->set_start (new_start);
3142 } else if (new_end > copy_location->start()) {
3143 copy_location->set_end (new_end);
3144 } else if (newframe > 0) {
3145 _editor->snap_to (next, -1, true);
3146 copy_location->set_start (next);
3147 copy_location->set_end (newframe);
3152 update_item (copy_location);
3154 /* now lookup the actual GUI items used to display this
3155 * location and move them to wherever the copy of the location
3156 * is now. This means that the logic in ARDOUR::Location is
3157 * still enforced, even though we are not (yet) modifying
3158 * the real Location itself.
3161 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3164 lm->set_position (copy_location->start(), copy_location->end());
3169 assert (!_copied_locations.empty());
3171 show_verbose_cursor_time (newframe);
3175 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3177 if (!movement_occurred) {
3179 if (was_double_click()) {
3180 _editor->rename_marker (_marker);
3184 /* just a click, do nothing but finish
3185 off the selection process
3188 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3191 case Selection::Set:
3192 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3193 _editor->selection->set (_marker);
3197 case Selection::Toggle:
3198 /* we toggle on the button release, click only */
3199 _editor->selection->toggle (_marker);
3202 case Selection::Extend:
3203 case Selection::Add:
3210 _editor->_dragging_edit_point = false;
3212 _editor->begin_reversible_command ( _("move marker") );
3213 XMLNode &before = _editor->session()->locations()->get_state();
3215 MarkerSelection::iterator i;
3216 CopiedLocationInfo::iterator x;
3219 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3220 x != _copied_locations.end() && i != _editor->selection->markers.end();
3223 Location * location = _editor->find_location_from_marker (*i, is_start);
3227 if (location->locked()) {
3231 if (location->is_mark()) {
3232 location->set_start (((*x).location)->start());
3234 location->set (((*x).location)->start(), ((*x).location)->end());
3239 XMLNode &after = _editor->session()->locations()->get_state();
3240 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3241 _editor->commit_reversible_command ();
3245 MarkerDrag::aborted (bool)
3251 MarkerDrag::update_item (Location*)
3256 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3258 _cumulative_x_drag (0),
3259 _cumulative_y_drag (0)
3261 if (_zero_gain_fraction < 0.0) {
3262 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3265 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3267 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3273 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3275 Drag::start_grab (event, _editor->cursors()->fader);
3277 // start the grab at the center of the control point so
3278 // the point doesn't 'jump' to the mouse after the first drag
3279 _fixed_grab_x = _point->get_x();
3280 _fixed_grab_y = _point->get_y();
3282 float const fraction = 1 - (_point->get_y() / _point->line().height());
3284 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3286 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3287 event->button.x + 10, event->button.y + 10);
3289 _editor->verbose_cursor()->show ();
3291 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3293 if (!_point->can_slide ()) {
3294 _x_constrained = true;
3299 ControlPointDrag::motion (GdkEvent* event, bool)
3301 double dx = _drags->current_pointer_x() - last_pointer_x();
3302 double dy = current_pointer_y() - last_pointer_y();
3304 if (event->button.state & Keyboard::SecondaryModifier) {
3309 /* coordinate in pixels relative to the start of the region (for region-based automation)
3310 or track (for track-based automation) */
3311 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3312 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3314 // calculate zero crossing point. back off by .01 to stay on the
3315 // positive side of zero
3316 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3318 // make sure we hit zero when passing through
3319 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3323 if (_x_constrained) {
3326 if (_y_constrained) {
3330 _cumulative_x_drag = cx - _fixed_grab_x;
3331 _cumulative_y_drag = cy - _fixed_grab_y;
3335 cy = min ((double) _point->line().height(), cy);
3337 framepos_t cx_frames = _editor->pixel_to_sample (cx);
3339 if (!_x_constrained) {
3340 _editor->snap_to_with_modifier (cx_frames, event);
3343 cx_frames = min (cx_frames, _point->line().maximum_time());
3345 float const fraction = 1.0 - (cy / _point->line().height());
3347 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3349 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3353 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3355 if (!movement_occurred) {
3359 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3360 _editor->reset_point_selection ();
3364 motion (event, false);
3367 _point->line().end_drag (_pushing, _final_index);
3368 _editor->session()->commit_reversible_command ();
3372 ControlPointDrag::aborted (bool)
3374 _point->line().reset ();
3378 ControlPointDrag::active (Editing::MouseMode m)
3380 if (m == Editing::MouseGain) {
3381 /* always active in mouse gain */
3385 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3386 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3389 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3392 _cumulative_y_drag (0)
3394 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3398 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3400 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3403 _item = &_line->grab_item ();
3405 /* need to get x coordinate in terms of parent (TimeAxisItemView)
3406 origin, and ditto for y.
3409 double cx = event->button.x;
3410 double cy = event->button.y;
3412 _line->parent_group().canvas_to_item (cx, cy);
3414 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3419 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3420 /* no adjacent points */
3424 Drag::start_grab (event, _editor->cursors()->fader);
3426 /* store grab start in parent frame */
3431 double fraction = 1.0 - (cy / _line->height());
3433 _line->start_drag_line (before, after, fraction);
3435 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3436 event->button.x + 10, event->button.y + 10);
3438 _editor->verbose_cursor()->show ();
3442 LineDrag::motion (GdkEvent* event, bool)
3444 double dy = current_pointer_y() - last_pointer_y();
3446 if (event->button.state & Keyboard::SecondaryModifier) {
3450 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3452 _cumulative_y_drag = cy - _fixed_grab_y;
3455 cy = min ((double) _line->height(), cy);
3457 double const fraction = 1.0 - (cy / _line->height());
3460 /* we are ignoring x position for this drag, so we can just pass in anything */
3461 _line->drag_motion (0, fraction, true, false, ignored);
3463 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3467 LineDrag::finished (GdkEvent* event, bool movement_occured)
3469 if (movement_occured) {
3470 motion (event, false);
3471 _line->end_drag (false, 0);
3473 /* add a new control point on the line */
3475 AutomationTimeAxisView* atv;
3477 _line->end_drag (false, 0);
3479 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3480 framepos_t where = _editor->window_event_sample (event, 0, 0);
3481 atv->add_automation_event (event, where, event->button.y, false);
3485 _editor->session()->commit_reversible_command ();
3489 LineDrag::aborted (bool)
3494 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3497 _cumulative_x_drag (0)
3499 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3503 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3505 Drag::start_grab (event);
3507 _line = reinterpret_cast<Line*> (_item);
3510 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3512 double cx = event->button.x;
3513 double cy = event->button.y;
3515 _item->parent()->canvas_to_item (cx, cy);
3517 /* store grab start in parent frame */
3518 _region_view_grab_x = cx;
3520 _before = *(float*) _item->get_data ("position");
3522 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3524 _max_x = _editor->sample_to_pixel(_arv->get_duration());
3528 FeatureLineDrag::motion (GdkEvent*, bool)
3530 double dx = _drags->current_pointer_x() - last_pointer_x();
3532 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3534 _cumulative_x_drag += dx;
3536 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3545 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3547 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3549 float *pos = new float;
3552 _line->set_data ("position", pos);
3558 FeatureLineDrag::finished (GdkEvent*, bool)
3560 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3561 _arv->update_transient(_before, _before);
3565 FeatureLineDrag::aborted (bool)
3570 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3572 , _vertical_only (false)
3574 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3578 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3580 Drag::start_grab (event);
3581 show_verbose_cursor_time (adjusted_current_frame (event));
3585 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3592 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3594 framepos_t grab = grab_frame ();
3595 if (Config->get_rubberbanding_snaps_to_grid ()) {
3596 _editor->snap_to_with_modifier (grab, event);
3599 /* base start and end on initial click position */
3609 if (current_pointer_y() < grab_y()) {
3610 y1 = current_pointer_y();
3613 y2 = current_pointer_y();
3617 if (start != end || y1 != y2) {
3619 double x1 = _editor->sample_to_pixel (start);
3620 double x2 = _editor->sample_to_pixel (end);
3621 const double min_dimension = 2.0;
3623 if (_vertical_only) {
3624 /* fixed 10 pixel width */
3628 x2 = min (x1 - min_dimension, x2);
3630 x2 = max (x1 + min_dimension, x2);
3635 y2 = min (y1 - min_dimension, y2);
3637 y2 = max (y1 + min_dimension, y2);
3640 /* translate rect into item space and set */
3642 ArdourCanvas::Rect r (x1, y1, x2, y2);
3644 /* this drag is a _trackview_only == true drag, so the y1 and
3645 * y2 (computed using current_pointer_y() and grab_y()) will be
3646 * relative to the top of the trackview group). The
3647 * rubberband rect has the same parent/scroll offset as the
3648 * the trackview group, so we can use the "r" rect directly
3649 * to set the shape of the rubberband.
3652 _editor->rubberband_rect->set (r);
3653 _editor->rubberband_rect->show();
3654 _editor->rubberband_rect->raise_to_top();
3656 show_verbose_cursor_time (pf);
3658 do_select_things (event, true);
3663 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3668 if (grab_frame() < last_pointer_frame()) {
3670 x2 = last_pointer_frame ();
3673 x1 = last_pointer_frame ();
3679 if (current_pointer_y() < grab_y()) {
3680 y1 = current_pointer_y();
3683 y2 = current_pointer_y();
3687 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3691 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3693 if (movement_occurred) {
3695 motion (event, false);
3696 do_select_things (event, false);
3702 bool do_deselect = true;
3703 MidiTimeAxisView* mtv;
3705 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3707 if (_editor->selection->empty()) {
3708 /* nothing selected */
3709 add_midi_region (mtv);
3710 do_deselect = false;
3714 /* do not deselect if Primary or Tertiary (toggle-select or
3715 * extend-select are pressed.
3718 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
3719 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
3726 _editor->rubberband_rect->hide();
3730 RubberbandSelectDrag::aborted (bool)
3732 _editor->rubberband_rect->hide ();
3735 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3736 : RegionDrag (e, i, p, v)
3738 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3742 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3744 Drag::start_grab (event, cursor);
3746 show_verbose_cursor_time (adjusted_current_frame (event));
3750 TimeFXDrag::motion (GdkEvent* event, bool)
3752 RegionView* rv = _primary;
3753 StreamView* cv = rv->get_time_axis_view().view ();
3755 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3756 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3757 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3759 framepos_t const pf = adjusted_current_frame (event);
3761 if (pf > rv->region()->position()) {
3762 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3765 show_verbose_cursor_time (pf);
3769 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3771 _primary->get_time_axis_view().hide_timestretch ();
3773 if (!movement_occurred) {
3777 if (last_pointer_frame() < _primary->region()->position()) {
3778 /* backwards drag of the left edge - not usable */
3782 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3784 float percentage = (double) newlen / (double) _primary->region()->length();
3786 #ifndef USE_RUBBERBAND
3787 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3788 if (_primary->region()->data_type() == DataType::AUDIO) {
3789 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3793 if (!_editor->get_selection().regions.empty()) {
3794 /* primary will already be included in the selection, and edit
3795 group shared editing will propagate selection across
3796 equivalent regions, so just use the current region
3800 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3801 error << _("An error occurred while executing time stretch operation") << endmsg;
3807 TimeFXDrag::aborted (bool)
3809 _primary->get_time_axis_view().hide_timestretch ();
3812 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3815 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3819 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3821 Drag::start_grab (event);
3825 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3827 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3831 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3833 if (movement_occurred && _editor->session()) {
3834 /* make sure we stop */
3835 _editor->session()->request_transport_speed (0.0);
3840 ScrubDrag::aborted (bool)
3845 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3850 , _original_pointer_time_axis (-1)
3851 , _last_pointer_time_axis (-1)
3852 , _time_selection_at_start (!_editor->get_selection().time.empty())
3854 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3856 if (_time_selection_at_start) {
3857 start_at_start = _editor->get_selection().time.start();
3858 end_at_start = _editor->get_selection().time.end_frame();
3863 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3865 if (_editor->session() == 0) {
3869 Gdk::Cursor* cursor = 0;
3871 switch (_operation) {
3872 case CreateSelection:
3873 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3878 cursor = _editor->cursors()->selector;
3879 Drag::start_grab (event, cursor);
3882 case SelectionStartTrim:
3883 if (_editor->clicked_axisview) {
3884 _editor->clicked_axisview->order_selection_trims (_item, true);
3886 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3889 case SelectionEndTrim:
3890 if (_editor->clicked_axisview) {
3891 _editor->clicked_axisview->order_selection_trims (_item, false);
3893 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3897 Drag::start_grab (event, cursor);
3900 case SelectionExtend:
3901 Drag::start_grab (event, cursor);
3905 if (_operation == SelectionMove) {
3906 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3908 show_verbose_cursor_time (adjusted_current_frame (event));
3911 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
3915 SelectionDrag::setup_pointer_frame_offset ()
3917 switch (_operation) {
3918 case CreateSelection:
3919 _pointer_frame_offset = 0;
3922 case SelectionStartTrim:
3924 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3927 case SelectionEndTrim:
3928 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3931 case SelectionExtend:
3937 SelectionDrag::motion (GdkEvent* event, bool first_move)
3939 framepos_t start = 0;
3941 framecnt_t length = 0;
3942 framecnt_t distance = 0;
3944 framepos_t const pending_position = adjusted_current_frame (event);
3946 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
3950 switch (_operation) {
3951 case CreateSelection:
3953 framepos_t grab = grab_frame ();
3956 grab = adjusted_current_frame (event, false);
3957 if (grab < pending_position) {
3958 _editor->snap_to (grab, -1);
3960 _editor->snap_to (grab, 1);
3964 if (pending_position < grab) {
3965 start = pending_position;
3968 end = pending_position;
3972 /* first drag: Either add to the selection
3973 or create a new selection
3980 /* adding to the selection */
3981 _editor->set_selected_track_as_side_effect (Selection::Add);
3982 _editor->clicked_selection = _editor->selection->add (start, end);
3989 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3990 _editor->set_selected_track_as_side_effect (Selection::Set);
3993 _editor->clicked_selection = _editor->selection->set (start, end);
3997 /* select all tracks within the rectangle that we've marked out so far */
3998 TrackViewList to_be_added_to_selection;
3999 TrackViewList to_be_removed_from_selection;
4000 TrackViewList& all_tracks (_editor->track_views);
4002 ArdourCanvas::Coord const top = grab_y();
4003 ArdourCanvas::Coord const bottom = current_pointer_y();
4005 if (top >= 0 && bottom >= 0) {
4007 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4009 if ((*i)->covered_by_y_range (top, bottom)) {
4010 if (!(*i)->get_selected()) {
4011 to_be_added_to_selection.push_back (*i);
4014 if ((*i)->get_selected()) {
4015 to_be_removed_from_selection.push_back (*i);
4020 if (!to_be_added_to_selection.empty()) {
4021 _editor->selection->add (to_be_added_to_selection);
4024 if (!to_be_removed_from_selection.empty()) {
4025 _editor->selection->remove (to_be_removed_from_selection);
4031 case SelectionStartTrim:
4033 start = _editor->selection->time[_editor->clicked_selection].start;
4034 end = _editor->selection->time[_editor->clicked_selection].end;
4036 if (pending_position > end) {
4039 start = pending_position;
4043 case SelectionEndTrim:
4045 start = _editor->selection->time[_editor->clicked_selection].start;
4046 end = _editor->selection->time[_editor->clicked_selection].end;
4048 if (pending_position < start) {
4051 end = pending_position;
4058 start = _editor->selection->time[_editor->clicked_selection].start;
4059 end = _editor->selection->time[_editor->clicked_selection].end;
4061 length = end - start;
4062 distance = pending_position - start;
4063 start = pending_position;
4064 _editor->snap_to (start);
4066 end = start + length;
4070 case SelectionExtend:
4075 switch (_operation) {
4077 if (_time_selection_at_start) {
4078 _editor->selection->move_time (distance);
4082 _editor->selection->replace (_editor->clicked_selection, start, end);
4086 if (_operation == SelectionMove) {
4087 show_verbose_cursor_time(start);
4089 show_verbose_cursor_time(pending_position);
4094 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4096 Session* s = _editor->session();
4098 if (movement_occurred) {
4099 motion (event, false);
4100 /* XXX this is not object-oriented programming at all. ick */
4101 if (_editor->selection->time.consolidate()) {
4102 _editor->selection->TimeChanged ();
4105 /* XXX what if its a music time selection? */
4107 if ( s->get_play_range() && s->transport_rolling() ) {
4108 s->request_play_range (&_editor->selection->time, true);
4110 if (Config->get_always_play_range() && !s->transport_rolling()) {
4111 s->request_locate (_editor->get_selection().time.start());
4117 /* just a click, no pointer movement.
4120 if (_operation == SelectionExtend) {
4121 if (_time_selection_at_start) {
4122 framepos_t pos = adjusted_current_frame (event, false);
4123 framepos_t start = min (pos, start_at_start);
4124 framepos_t end = max (pos, end_at_start);
4125 _editor->selection->set (start, end);
4128 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4129 if (_editor->clicked_selection) {
4130 _editor->selection->remove (_editor->clicked_selection);
4133 if (!_editor->clicked_selection) {
4134 _editor->selection->clear_time();
4139 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4140 _editor->selection->set (_editor->clicked_axisview);
4143 if (s && s->get_play_range () && s->transport_rolling()) {
4144 s->request_stop (false, false);
4149 _editor->stop_canvas_autoscroll ();
4150 _editor->clicked_selection = 0;
4154 SelectionDrag::aborted (bool)
4159 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4160 : Drag (e, i, false),
4164 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4166 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4167 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4168 physical_screen_height (_editor->get_window())));
4169 _drag_rect->hide ();
4171 _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4172 _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4176 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4178 if (_editor->session() == 0) {
4182 Gdk::Cursor* cursor = 0;
4184 if (!_editor->temp_location) {
4185 _editor->temp_location = new Location (*_editor->session());
4188 switch (_operation) {
4189 case CreateRangeMarker:
4190 case CreateTransportMarker:
4191 case CreateCDMarker:
4193 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4198 cursor = _editor->cursors()->selector;
4202 Drag::start_grab (event, cursor);
4204 show_verbose_cursor_time (adjusted_current_frame (event));
4208 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4210 framepos_t start = 0;
4212 ArdourCanvas::Rectangle *crect;
4214 switch (_operation) {
4215 case CreateRangeMarker:
4216 crect = _editor->range_bar_drag_rect;
4218 case CreateTransportMarker:
4219 crect = _editor->transport_bar_drag_rect;
4221 case CreateCDMarker:
4222 crect = _editor->cd_marker_bar_drag_rect;
4225 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4230 framepos_t const pf = adjusted_current_frame (event);
4232 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4233 framepos_t grab = grab_frame ();
4234 _editor->snap_to (grab);
4236 if (pf < grab_frame()) {
4244 /* first drag: Either add to the selection
4245 or create a new selection.
4250 _editor->temp_location->set (start, end);
4254 update_item (_editor->temp_location);
4256 //_drag_rect->raise_to_top();
4262 _editor->temp_location->set (start, end);
4264 double x1 = _editor->sample_to_pixel (start);
4265 double x2 = _editor->sample_to_pixel (end);
4269 update_item (_editor->temp_location);
4272 show_verbose_cursor_time (pf);
4277 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4279 Location * newloc = 0;
4283 if (movement_occurred) {
4284 motion (event, false);
4287 switch (_operation) {
4288 case CreateRangeMarker:
4289 case CreateCDMarker:
4291 _editor->begin_reversible_command (_("new range marker"));
4292 XMLNode &before = _editor->session()->locations()->get_state();
4293 _editor->session()->locations()->next_available_name(rangename,"unnamed");
4294 if (_operation == CreateCDMarker) {
4295 flags = Location::IsRangeMarker | Location::IsCDMarker;
4296 _editor->cd_marker_bar_drag_rect->hide();
4299 flags = Location::IsRangeMarker;
4300 _editor->range_bar_drag_rect->hide();
4302 newloc = new Location (
4303 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4306 _editor->session()->locations()->add (newloc, true);
4307 XMLNode &after = _editor->session()->locations()->get_state();
4308 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4309 _editor->commit_reversible_command ();
4313 case CreateTransportMarker:
4314 // popup menu to pick loop or punch
4315 _editor->new_transport_marker_context_menu (&event->button, _item);
4321 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4323 if (_operation == CreateTransportMarker) {
4325 /* didn't drag, so just locate */
4327 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4329 } else if (_operation == CreateCDMarker) {
4331 /* didn't drag, but mark is already created so do
4334 } else { /* operation == CreateRangeMarker */
4340 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4342 if (end == max_framepos) {
4343 end = _editor->session()->current_end_frame ();
4346 if (start == max_framepos) {
4347 start = _editor->session()->current_start_frame ();
4350 switch (_editor->mouse_mode) {
4352 /* find the two markers on either side and then make the selection from it */
4353 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4357 /* find the two markers on either side of the click and make the range out of it */
4358 _editor->selection->set (start, end);
4367 _editor->stop_canvas_autoscroll ();
4371 RangeMarkerBarDrag::aborted (bool)
4377 RangeMarkerBarDrag::update_item (Location* location)
4379 double const x1 = _editor->sample_to_pixel (location->start());
4380 double const x2 = _editor->sample_to_pixel (location->end());
4382 _drag_rect->set_x0 (x1);
4383 _drag_rect->set_x1 (x2);
4386 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4390 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4394 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4396 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4397 Drag::start_grab (event, _editor->cursors()->zoom_out);
4400 Drag::start_grab (event, _editor->cursors()->zoom_in);
4404 show_verbose_cursor_time (adjusted_current_frame (event));
4408 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4413 framepos_t const pf = adjusted_current_frame (event);
4415 framepos_t grab = grab_frame ();
4416 _editor->snap_to_with_modifier (grab, event);
4418 /* base start and end on initial click position */
4430 _editor->zoom_rect->show();
4431 _editor->zoom_rect->raise_to_top();
4434 _editor->reposition_zoom_rect(start, end);
4436 show_verbose_cursor_time (pf);
4441 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4443 if (movement_occurred) {
4444 motion (event, false);
4446 if (grab_frame() < last_pointer_frame()) {
4447 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4449 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4452 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4453 _editor->tav_zoom_step (_zoom_out);
4455 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4459 _editor->zoom_rect->hide();
4463 MouseZoomDrag::aborted (bool)
4465 _editor->zoom_rect->hide ();
4468 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4470 , _cumulative_dx (0)
4471 , _cumulative_dy (0)
4473 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4475 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4477 _region = &_primary->region_view ();
4478 _note_height = _region->midi_stream_view()->note_height ();
4482 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4484 Drag::start_grab (event);
4486 if (!(_was_selected = _primary->selected())) {
4488 /* tertiary-click means extend selection - we'll do that on button release,
4489 so don't add it here, because otherwise we make it hard to figure
4490 out the "extend-to" range.
4493 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4496 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4499 _region->note_selected (_primary, true);
4501 _region->unique_select (_primary);
4507 /** @return Current total drag x change in frames */
4509 NoteDrag::total_dx () const
4512 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4514 /* primary note time */
4515 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4517 /* new time of the primary note in session frames */
4518 frameoffset_t st = n + dx;
4520 framepos_t const rp = _region->region()->position ();
4522 /* prevent the note being dragged earlier than the region's position */
4525 /* snap and return corresponding delta */
4526 return _region->snap_frame_to_frame (st - rp) + rp - n;
4529 /** @return Current total drag y change in note number */
4531 NoteDrag::total_dy () const
4533 MidiStreamView* msv = _region->midi_stream_view ();
4534 double const y = _region->midi_view()->y_position ();
4535 /* new current note */
4536 uint8_t n = msv->y_to_note (current_pointer_y () - y);
4538 n = max (msv->lowest_note(), n);
4539 n = min (msv->highest_note(), n);
4540 /* and work out delta */
4541 return n - msv->y_to_note (grab_y() - y);
4545 NoteDrag::motion (GdkEvent *, bool)
4547 /* Total change in x and y since the start of the drag */
4548 frameoffset_t const dx = total_dx ();
4549 int8_t const dy = total_dy ();
4551 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4552 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4553 double const tdy = -dy * _note_height - _cumulative_dy;
4556 _cumulative_dx += tdx;
4557 _cumulative_dy += tdy;
4559 int8_t note_delta = total_dy();
4561 _region->move_selection (tdx, tdy, note_delta);
4563 /* the new note value may be the same as the old one, but we
4564 * don't know what that means because the selection may have
4565 * involved more than one note and we might be doing something
4566 * odd with them. so show the note value anyway, always.
4570 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4572 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4573 (int) floor ((double)new_note));
4575 show_verbose_cursor_text (buf);
4580 NoteDrag::finished (GdkEvent* ev, bool moved)
4583 /* no motion - select note */
4585 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4586 _editor->current_mouse_mode() == Editing::MouseDraw) {
4588 if (_was_selected) {
4589 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4591 _region->note_deselected (_primary);
4594 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4595 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4597 if (!extend && !add && _region->selection_size() > 1) {
4598 _region->unique_select (_primary);
4599 } else if (extend) {
4600 _region->note_selected (_primary, true, true);
4602 /* it was added during button press */
4607 _region->note_dropped (_primary, total_dx(), total_dy());
4612 NoteDrag::aborted (bool)
4617 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4618 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4619 : Drag (editor, atv->base_item ())
4621 , _nothing_to_drag (false)
4623 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4624 y_origin = atv->y_position();
4625 setup (atv->lines ());
4628 /** Make an AutomationRangeDrag for region gain lines */
4629 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4630 : Drag (editor, rv->get_canvas_group ())
4632 , _nothing_to_drag (false)
4634 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4636 list<boost::shared_ptr<AutomationLine> > lines;
4637 lines.push_back (rv->get_gain_line ());
4638 y_origin = rv->get_time_axis_view().y_position();
4642 /** @param lines AutomationLines to drag.
4643 * @param offset Offset from the session start to the points in the AutomationLines.
4646 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4648 /* find the lines that overlap the ranges being dragged */
4649 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4650 while (i != lines.end ()) {
4651 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4654 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4656 /* check this range against all the AudioRanges that we are using */
4657 list<AudioRange>::const_iterator k = _ranges.begin ();
4658 while (k != _ranges.end()) {
4659 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4665 /* add it to our list if it overlaps at all */
4666 if (k != _ranges.end()) {
4671 _lines.push_back (n);
4677 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4681 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4683 return 1.0 - ((global_y - y_origin) / line->height());
4687 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4689 Drag::start_grab (event, cursor);
4691 /* Get line states before we start changing things */
4692 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4693 i->state = &i->line->get_state ();
4694 i->original_fraction = y_fraction (i->line, current_pointer_y());
4697 if (_ranges.empty()) {
4699 /* No selected time ranges: drag all points */
4700 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4701 uint32_t const N = i->line->npoints ();
4702 for (uint32_t j = 0; j < N; ++j) {
4703 i->points.push_back (i->line->nth (j));
4709 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4711 framecnt_t const half = (i->start + i->end) / 2;
4713 /* find the line that this audio range starts in */
4714 list<Line>::iterator j = _lines.begin();
4715 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4719 if (j != _lines.end()) {
4720 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4722 /* j is the line that this audio range starts in; fade into it;
4723 64 samples length plucked out of thin air.
4726 framepos_t a = i->start + 64;
4731 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4732 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4734 the_list->add (p, the_list->eval (p));
4735 the_list->add (q, the_list->eval (q));
4738 /* same thing for the end */
4741 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4745 if (j != _lines.end()) {
4746 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4748 /* j is the line that this audio range starts in; fade out of it;
4749 64 samples length plucked out of thin air.
4752 framepos_t b = i->end - 64;
4757 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4758 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4760 the_list->add (p, the_list->eval (p));
4761 the_list->add (q, the_list->eval (q));
4765 _nothing_to_drag = true;
4767 /* Find all the points that should be dragged and put them in the relevant
4768 points lists in the Line structs.
4771 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4773 uint32_t const N = i->line->npoints ();
4774 for (uint32_t j = 0; j < N; ++j) {
4776 /* here's a control point on this line */
4777 ControlPoint* p = i->line->nth (j);
4778 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4780 /* see if it's inside a range */
4781 list<AudioRange>::const_iterator k = _ranges.begin ();
4782 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4786 if (k != _ranges.end()) {
4787 /* dragging this point */
4788 _nothing_to_drag = false;
4789 i->points.push_back (p);
4795 if (_nothing_to_drag) {
4799 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4800 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
4805 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4807 if (_nothing_to_drag) {
4811 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4812 float const f = y_fraction (l->line, current_pointer_y());
4813 /* we are ignoring x position for this drag, so we can just pass in anything */
4815 l->line->drag_motion (0, f, true, false, ignored);
4816 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4821 AutomationRangeDrag::finished (GdkEvent* event, bool)
4823 if (_nothing_to_drag) {
4827 motion (event, false);
4828 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4829 i->line->end_drag (false, 0);
4832 _editor->session()->commit_reversible_command ();
4836 AutomationRangeDrag::aborted (bool)
4838 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4843 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4846 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4847 layer = v->region()->layer ();
4848 initial_y = v->get_canvas_group()->position().y;
4849 initial_playlist = v->region()->playlist ();
4850 initial_position = v->region()->position ();
4851 initial_end = v->region()->position () + v->region()->length ();
4854 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4855 : Drag (e, i->canvas_item ())
4858 , _cumulative_dx (0)
4860 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4861 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4866 PatchChangeDrag::motion (GdkEvent* ev, bool)
4868 framepos_t f = adjusted_current_frame (ev);
4869 boost::shared_ptr<Region> r = _region_view->region ();
4870 f = max (f, r->position ());
4871 f = min (f, r->last_frame ());
4873 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4874 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4875 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4876 _cumulative_dx = dxu;
4880 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4882 if (!movement_occurred) {
4886 boost::shared_ptr<Region> r (_region_view->region ());
4887 framepos_t f = adjusted_current_frame (ev);
4888 f = max (f, r->position ());
4889 f = min (f, r->last_frame ());
4891 _region_view->move_patch_change (
4893 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4898 PatchChangeDrag::aborted (bool)
4900 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4904 PatchChangeDrag::setup_pointer_frame_offset ()
4906 boost::shared_ptr<Region> region = _region_view->region ();
4907 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4910 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4911 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4918 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4920 framepos_t const p = _region_view->region()->position ();
4921 double const y = _region_view->midi_view()->y_position ();
4923 x1 = max ((framepos_t) 0, x1 - p);
4924 x2 = max ((framepos_t) 0, x2 - p);
4925 y1 = max (0.0, y1 - y);
4926 y2 = max (0.0, y2 - y);
4928 _region_view->update_drag_selection (
4929 _editor->sample_to_pixel (x1),
4930 _editor->sample_to_pixel (x2),
4933 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4938 MidiRubberbandSelectDrag::deselect_things ()
4943 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4944 : RubberbandSelectDrag (e, rv->get_canvas_group ())
4947 _vertical_only = true;
4951 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4953 double const y = _region_view->midi_view()->y_position ();
4955 y1 = max (0.0, y1 - y);
4956 y2 = max (0.0, y2 - y);
4958 _region_view->update_vertical_drag_selection (
4961 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4966 MidiVerticalSelectDrag::deselect_things ()
4971 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4972 : RubberbandSelectDrag (e, i)
4978 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4980 if (drag_in_progress) {
4981 /* We just want to select things at the end of the drag, not during it */
4985 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4987 _editor->begin_reversible_command (_("rubberband selection"));
4988 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4989 _editor->commit_reversible_command ();
4993 EditorRubberbandSelectDrag::deselect_things ()
4995 if (!getenv("ARDOUR_SAE")) {
4996 _editor->selection->clear_tracks();
4998 _editor->selection->clear_regions();
4999 _editor->selection->clear_points ();
5000 _editor->selection->clear_lines ();
5003 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5011 NoteCreateDrag::~NoteCreateDrag ()
5017 NoteCreateDrag::grid_frames (framepos_t t) const
5020 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
5025 return _region_view->region_beats_to_region_frames (grid_beats);
5029 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5031 Drag::start_grab (event, cursor);
5033 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5035 framepos_t pf = _drags->current_pointer_frame ();
5036 framecnt_t const g = grid_frames (pf);
5038 /* Hack so that we always snap to the note that we are over, instead of snapping
5039 to the next one if we're more than halfway through the one we're over.
5041 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5045 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5047 MidiStreamView* sv = _region_view->midi_stream_view ();
5048 double const x = _editor->sample_to_pixel (_note[0]);
5049 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5051 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5052 _drag_rect->set_outline_all ();
5053 _drag_rect->set_outline_color (0xffffff99);
5054 _drag_rect->set_fill_color (0xffffff66);
5058 NoteCreateDrag::motion (GdkEvent* event, bool)
5060 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5061 double const x = _editor->sample_to_pixel (_note[1]);
5062 if (_note[1] > _note[0]) {
5063 _drag_rect->set_x1 (x);
5065 _drag_rect->set_x0 (x);
5070 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5072 if (!had_movement) {
5076 framepos_t const start = min (_note[0], _note[1]);
5077 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5079 framecnt_t const g = grid_frames (start);
5080 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5082 if (_editor->snap_mode() == SnapNormal && length < g) {
5083 length = g - one_tick;
5086 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5088 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5092 NoteCreateDrag::y_to_region (double y) const
5095 _region_view->get_canvas_group()->canvas_to_item (x, y);
5100 NoteCreateDrag::aborted (bool)
5105 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5110 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5114 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5116 Drag::start_grab (event, cursor);
5120 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5126 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5129 distance = _drags->current_pointer_x() - grab_x();
5130 len = ar->fade_in()->back()->when;
5132 distance = grab_x() - _drags->current_pointer_x();
5133 len = ar->fade_out()->back()->when;
5136 /* how long should it be ? */
5138 new_length = len + _editor->pixel_to_sample (distance);
5140 /* now check with the region that this is legal */
5142 new_length = ar->verify_xfade_bounds (new_length, start);
5145 arv->reset_fade_in_shape_width (ar, new_length);
5147 arv->reset_fade_out_shape_width (ar, new_length);
5152 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5158 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5161 distance = _drags->current_pointer_x() - grab_x();
5162 len = ar->fade_in()->back()->when;
5164 distance = grab_x() - _drags->current_pointer_x();
5165 len = ar->fade_out()->back()->when;
5168 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5170 _editor->begin_reversible_command ("xfade trim");
5171 ar->playlist()->clear_owned_changes ();
5174 ar->set_fade_in_length (new_length);
5176 ar->set_fade_out_length (new_length);
5179 /* Adjusting the xfade may affect other regions in the playlist, so we need
5180 to get undo Commands from the whole playlist rather than just the
5184 vector<Command*> cmds;
5185 ar->playlist()->rdiff (cmds);
5186 _editor->session()->add_commands (cmds);
5187 _editor->commit_reversible_command ();
5192 CrossfadeEdgeDrag::aborted (bool)
5195 arv->redraw_start_xfade ();
5197 arv->redraw_end_xfade ();