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/canvas.h"
44 #include "canvas/scroll_group.h"
49 #include "audio_region_view.h"
50 #include "automation_region_view.h"
51 #include "midi_region_view.h"
52 #include "ardour_ui.h"
53 #include "gui_thread.h"
54 #include "control_point.h"
55 #include "region_gain_line.h"
56 #include "editor_drag.h"
57 #include "audio_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "selection.h"
60 #include "midi_selection.h"
61 #include "automation_time_axis.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "verbose_cursor.h"
70 using namespace ARDOUR;
73 using namespace Gtkmm2ext;
74 using namespace Editing;
75 using namespace ArdourCanvas;
77 using Gtkmm2ext::Keyboard;
79 double ControlPointDrag::_zero_gain_fraction = -1.0;
81 DragManager::DragManager (Editor* e)
84 , _current_pointer_frame (0)
88 DragManager::~DragManager ()
93 /** Call abort for each active drag */
99 cerr << "Aborting drag\n";
101 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
106 if (!_drags.empty ()) {
107 _editor->set_follow_playhead (_old_follow_playhead, false);
111 _editor->abort_reversible_command();
117 DragManager::add (Drag* d)
119 d->set_manager (this);
120 _drags.push_back (d);
124 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
126 d->set_manager (this);
127 _drags.push_back (d);
132 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
134 /* Prevent follow playhead during the drag to be nice to the user */
135 _old_follow_playhead = _editor->follow_playhead ();
136 _editor->set_follow_playhead (false);
138 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
140 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
141 (*i)->start_grab (e, c);
145 /** Call end_grab for each active drag.
146 * @return true if any drag reported movement having occurred.
149 DragManager::end_grab (GdkEvent* e)
154 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
155 bool const t = (*i)->end_grab (e);
166 _editor->set_follow_playhead (_old_follow_playhead, false);
172 DragManager::mark_double_click ()
174 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
175 (*i)->set_double_click (true);
180 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
184 /* calling this implies that we expect the event to have canvas
187 * Can we guarantee that this is true?
190 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
192 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
193 bool const t = (*i)->motion_handler (e, from_autoscroll);
194 /* run all handlers; return true if at least one of them
195 returns true (indicating that the event has been handled).
207 DragManager::have_item (ArdourCanvas::Item* i) const
209 list<Drag*>::const_iterator j = _drags.begin ();
210 while (j != _drags.end() && (*j)->item () != i) {
214 return j != _drags.end ();
217 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
220 , _pointer_frame_offset (0)
221 , _trackview_only (trackview_only)
222 , _move_threshold_passed (false)
223 , _starting_point_passed (false)
224 , _initially_vertical (false)
225 , _was_double_click (false)
226 , _raw_grab_frame (0)
228 , _last_pointer_frame (0)
235 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
241 _cursor_ctx = CursorContext::create (*_editor, cursor);
243 _cursor_ctx->change (cursor);
250 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
252 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
254 if (Keyboard::is_button2_event (&event->button)) {
255 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
256 _y_constrained = true;
257 _x_constrained = false;
259 _y_constrained = false;
260 _x_constrained = true;
263 _x_constrained = false;
264 _y_constrained = false;
267 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
269 setup_pointer_frame_offset ();
270 _grab_frame = adjusted_frame (_raw_grab_frame, event);
271 _last_pointer_frame = _grab_frame;
272 _last_pointer_x = _grab_x;
274 if (_trackview_only) {
275 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
278 _last_pointer_y = _grab_y;
282 if (!_editor->cursors()->is_invalid (cursor)) {
283 /* CAIROCANVAS need a variant here that passes *cursor */
284 _cursor_ctx = CursorContext::create (*_editor, cursor);
287 if (_editor->session() && _editor->session()->transport_rolling()) {
290 _was_rolling = false;
293 switch (_editor->snap_type()) {
294 case SnapToRegionStart:
295 case SnapToRegionEnd:
296 case SnapToRegionSync:
297 case SnapToRegionBoundary:
298 _editor->build_region_boundary_cache ();
305 /** Call to end a drag `successfully'. Ungrabs item and calls
306 * subclass' finished() method.
308 * @param event GDK event, or 0.
309 * @return true if some movement occurred, otherwise false.
312 Drag::end_grab (GdkEvent* event)
314 _editor->stop_canvas_autoscroll ();
318 finished (event, _move_threshold_passed);
320 _editor->verbose_cursor()->hide ();
323 return _move_threshold_passed;
327 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
331 if (f > _pointer_frame_offset) {
332 pos = f - _pointer_frame_offset;
336 _editor->snap_to_with_modifier (pos, event);
343 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
345 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
349 Drag::current_pointer_x() const
351 return _drags->current_pointer_x ();
355 Drag::current_pointer_y () const
357 if (!_trackview_only) {
358 return _drags->current_pointer_y ();
361 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
365 Drag::setup_snap_delta (framepos_t pos)
367 if (_editor->snap_delta () == SnapRelative) {
368 framepos_t temp = pos;
369 _editor->snap_to_no_magnets (temp);
370 _snap_delta = temp - pos;
375 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
377 /* check to see if we have moved in any way that matters since the last motion event */
378 if (_move_threshold_passed &&
379 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
380 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
384 pair<framecnt_t, int> const threshold = move_threshold ();
386 bool const old_move_threshold_passed = _move_threshold_passed;
388 if (!_move_threshold_passed) {
390 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
391 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
393 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
396 if (active (_editor->mouse_mode) && _move_threshold_passed) {
398 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
400 if (old_move_threshold_passed != _move_threshold_passed) {
404 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
405 _initially_vertical = true;
407 _initially_vertical = false;
411 if (!from_autoscroll) {
412 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
415 if (!_editor->autoscroll_active() || from_autoscroll) {
418 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
420 motion (event, first_move && !_starting_point_passed);
422 if (first_move && !_starting_point_passed) {
423 _starting_point_passed = true;
426 _last_pointer_x = _drags->current_pointer_x ();
427 _last_pointer_y = current_pointer_y ();
428 _last_pointer_frame = adjusted_current_frame (event);
438 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
446 aborted (_move_threshold_passed);
448 _editor->stop_canvas_autoscroll ();
449 _editor->verbose_cursor()->hide ();
453 Drag::show_verbose_cursor_time (framepos_t frame)
455 _editor->verbose_cursor()->set_time (frame);
456 _editor->verbose_cursor()->show ();
460 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
462 _editor->verbose_cursor()->set_duration (start, end);
463 _editor->verbose_cursor()->show ();
467 Drag::show_verbose_cursor_text (string const & text)
469 _editor->verbose_cursor()->set (text);
470 _editor->verbose_cursor()->show ();
473 boost::shared_ptr<Region>
474 Drag::add_midi_region (MidiTimeAxisView* view)
476 if (_editor->session()) {
477 const TempoMap& map (_editor->session()->tempo_map());
478 framecnt_t pos = grab_frame();
479 const Meter& m = map.meter_at (pos);
480 /* not that the frame rate used here can be affected by pull up/down which
483 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
484 return view->add_region (grab_frame(), len, true);
487 return boost::shared_ptr<Region>();
490 struct EditorOrderTimeAxisViewSorter {
491 bool operator() (TimeAxisView* a, TimeAxisView* b) {
492 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
493 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
495 return ra->route()->order_key () < rb->route()->order_key ();
499 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
504 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
506 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
507 as some of the regions we are dragging may be on such tracks.
510 TrackViewList track_views = _editor->track_views;
511 track_views.sort (EditorOrderTimeAxisViewSorter ());
513 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
514 _time_axis_views.push_back (*i);
516 TimeAxisView::Children children_list = (*i)->get_child_list ();
517 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
518 _time_axis_views.push_back (j->get());
522 /* the list of views can be empty at this point if this is a region list-insert drag
525 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
526 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
529 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
533 RegionDrag::region_going_away (RegionView* v)
535 list<DraggingView>::iterator i = _views.begin ();
536 while (i != _views.end() && i->view != v) {
540 if (i != _views.end()) {
545 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
546 * or -1 if it is not found.
549 RegionDrag::find_time_axis_view (TimeAxisView* t) const
552 int const N = _time_axis_views.size ();
553 while (i < N && _time_axis_views[i] != t) {
557 if (_time_axis_views[i] != t) {
564 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
565 : RegionDrag (e, i, p, v)
568 , _last_pointer_time_axis_view (0)
569 , _last_pointer_layer (0)
570 , _single_axis (false)
575 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
579 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
581 Drag::start_grab (event, cursor);
582 setup_snap_delta (_last_frame_position);
584 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
588 show_verbose_cursor_time (_last_frame_position);
590 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
592 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
593 assert(_last_pointer_time_axis_view >= 0);
594 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
599 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
601 /* compute the amount of pointer motion in frames, and where
602 the region would be if we moved it by that much.
604 *pending_region_position = adjusted_frame (_drags->current_pointer_frame () + snap_delta (), event, true);
606 framepos_t sync_frame;
607 framecnt_t sync_offset;
610 sync_offset = _primary->region()->sync_offset (sync_dir);
612 /* we don't handle a sync point that lies before zero.
614 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
616 sync_frame = *pending_region_position + (sync_dir * sync_offset);
618 _editor->snap_to_with_modifier (sync_frame, event);
620 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - snap_delta ();
623 *pending_region_position = _last_frame_position;
626 if (*pending_region_position > max_framepos - _primary->region()->length()) {
627 *pending_region_position = _last_frame_position;
632 /* in locked edit mode, reverse the usual meaning of _x_constrained */
633 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
635 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
637 /* x movement since last time (in pixels) */
638 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
640 /* total x movement */
641 framecnt_t total_dx = *pending_region_position;
642 if (regions_came_from_canvas()) {
643 total_dx = total_dx - grab_frame ();
646 /* check that no regions have gone off the start of the session */
647 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
648 if ((i->view->region()->position() + total_dx) < 0) {
650 *pending_region_position = _last_frame_position;
661 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
667 const int tavsize = _time_axis_views.size();
668 const int dt = delta > 0 ? +1 : -1;
670 int target = start + delta - skip;
672 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
673 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
675 while (current >= 0 && current != target) {
677 if (current < 0 && dt < 0) {
680 if (current >= tavsize && dt > 0) {
683 if (current < 0 || current >= tavsize) {
687 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
688 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
692 if (distance_only && current == start + delta) {
700 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
702 if (_y_constrained) {
706 const int tavsize = _time_axis_views.size();
707 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
708 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
709 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
711 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
712 /* already in the drop zone */
713 if (delta_track >= 0) {
714 /* downward motion - OK if others are still not in the dropzone */
723 } else if (n >= tavsize) {
724 /* downward motion into drop zone. That's fine. */
728 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
729 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
730 /* not a track, or the wrong type */
734 double const l = i->layer + delta_layer;
736 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
737 mode to allow the user to place a region below another on layer 0.
739 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
740 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
741 If it has, the layers will be munged later anyway, so it's ok.
747 /* all regions being dragged are ok with this change */
751 struct DraggingViewSorter {
752 bool operator() (const DraggingView& a, const DraggingView& b) {
753 return a.time_axis_view < b.time_axis_view;
758 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
760 double delta_layer = 0;
761 int delta_time_axis_view = 0;
762 int current_pointer_time_axis_view = -1;
764 assert (!_views.empty ());
768 if (initially_vertical()) {
769 _y_constrained = false;
770 _x_constrained = true;
772 _y_constrained = true;
773 _x_constrained = false;
778 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
780 /* Find the TimeAxisView that the pointer is now over */
781 const double cur_y = current_pointer_y ();
782 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
783 TimeAxisView* tv = r.first;
785 if (!tv && cur_y < 0) {
786 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
790 /* find drop-zone y-position */
791 Coord last_track_bottom_edge;
792 last_track_bottom_edge = 0;
793 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
794 if (!(*t)->hidden()) {
795 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
800 if (tv && tv->view()) {
801 /* the mouse is over a track */
802 double layer = r.second;
804 if (first_move && tv->view()->layer_display() == Stacked) {
805 tv->view()->set_layer_display (Expanded);
808 /* Here's the current pointer position in terms of time axis view and layer */
809 current_pointer_time_axis_view = find_time_axis_view (tv);
810 assert(current_pointer_time_axis_view >= 0);
812 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
814 /* Work out the change in y */
816 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
817 if (!rtv || !rtv->is_track()) {
818 /* ignore busses early on. we can't move any regions on them */
819 } else if (_last_pointer_time_axis_view < 0) {
820 /* Was in the drop-zone, now over a track.
821 * Hence it must be an upward move (from the bottom)
823 * track_index is still -1, so delta must be set to
824 * move up the correct number of tracks from the bottom.
826 * This is necessary because steps may be skipped if
827 * the bottom-most track is not a valid target and/or
828 * if there are hidden tracks at the bottom.
829 * Hence the initial offset (_ddropzone) as well as the
830 * last valid pointer position (_pdropzone) need to be
831 * taken into account.
833 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
835 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
838 /* TODO needs adjustment per DraggingView,
840 * e.g. select one region on the top-layer of a track
841 * and one region which is at the bottom-layer of another track
844 * Indicated drop-zones and layering is wrong.
845 * and may infer additional layers on the target-track
846 * (depending how many layers the original track had).
848 * Or select two regions (different layers) on a same track,
849 * move across a non-layer track.. -> layering info is lost.
850 * on drop either of the regions may be on top.
852 * Proposed solution: screw it :) well,
853 * don't use delta_layer, use an absolute value
854 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
855 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
856 * 3) iterate over all DraggingView, find the one that is over the track with most layers
857 * 4) proportionally scale layer to layers available on target
859 delta_layer = current_pointer_layer - _last_pointer_layer;
862 /* for automation lanes, there is a TimeAxisView but no ->view()
863 * if (!tv) -> dropzone
865 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
866 /* Moving into the drop-zone.. */
867 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
868 /* delta_time_axis_view may not be sufficient to move into the DZ
869 * the mouse may enter it, but it may not be a valid move due to
872 * -> remember the delta needed to move into the dropzone
874 _ddropzone = delta_time_axis_view;
875 /* ..but subtract hidden tracks (or routes) at the bottom.
876 * we silently move mover them
878 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
879 - _time_axis_views.size();
881 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
882 /* move around inside the zone.
883 * This allows to move further down until all regions are in the zone.
885 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
886 assert(ptr_y >= last_track_bottom_edge);
887 assert(_ddropzone > 0);
889 /* calculate mouse position in 'tracks' below last track. */
890 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
891 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
893 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
895 delta_time_axis_view = dzpos - _pdropzone;
896 } else if (dzpos < _pdropzone && _ndropzone > 0) {
897 // move up inside the DZ
898 delta_time_axis_view = dzpos - _pdropzone;
902 /* Work out the change in x */
903 framepos_t pending_region_position;
904 double const x_delta = compute_x_delta (event, &pending_region_position);
905 _last_frame_position = pending_region_position;
907 /* calculate hidden tracks in current y-axis delta */
909 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
910 /* The mouse is more than one track below the dropzone.
911 * distance calculation is not needed (and would not work, either
912 * because the dropzone is "packed").
914 * Except when [partially] moving regions out of dropzone in a large step.
915 * (the mouse may or may not remain in the DZ)
916 * Hidden tracks at the bottom of the TAV need to be skipped.
918 * This also handles the case if the mouse entered the DZ
919 * in a large step (exessive delta), either due to fast-movement,
920 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
922 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
923 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
925 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
926 -_time_axis_views.size() - dt;
929 else if (_last_pointer_time_axis_view < 0) {
930 /* Moving out of the zone. Check for hidden tracks at the bottom. */
931 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
932 -_time_axis_views.size() - delta_time_axis_view;
934 /* calculate hidden tracks that are skipped by the pointer movement */
935 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
936 - _last_pointer_time_axis_view
937 - delta_time_axis_view;
940 /* Verify change in y */
941 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
942 /* this y movement is not allowed, so do no y movement this time */
943 delta_time_axis_view = 0;
948 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
949 /* haven't reached next snap point, and we're not switching
950 trackviews nor layers. nothing to do.
955 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
956 PlaylistDropzoneMap playlist_dropzone_map;
957 _ndropzone = 0; // number of elements currently in the dropzone
960 /* sort views by time_axis.
961 * This retains track order in the dropzone, regardless
962 * of actual selection order
964 _views.sort (DraggingViewSorter());
966 /* count number of distinct tracks of all regions
967 * being dragged, used for dropzone.
970 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
971 if (i->time_axis_view != prev_track) {
972 prev_track = i->time_axis_view;
978 _views.back().time_axis_view -
979 _views.front().time_axis_view;
981 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
982 - _views.back().time_axis_view;
984 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
988 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
990 RegionView* rv = i->view;
995 if (rv->region()->locked() || rv->region()->video_locked()) {
1002 /* reparent the regionview into a group above all
1006 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1007 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1008 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1009 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1010 /* move the item so that it continues to appear at the
1011 same location now that its parent has changed.
1013 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1016 /* If we have moved tracks, we'll fudge the layer delta so that the
1017 region gets moved back onto layer 0 on its new track; this avoids
1018 confusion when dragging regions from non-zero layers onto different
1021 double this_delta_layer = delta_layer;
1022 if (delta_time_axis_view != 0) {
1023 this_delta_layer = - i->layer;
1026 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1028 int track_index = i->time_axis_view + this_delta_time_axis_view;
1029 assert(track_index >= 0);
1031 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1032 /* Track is in the Dropzone */
1034 i->time_axis_view = track_index;
1035 assert(i->time_axis_view >= (int) _time_axis_views.size());
1038 double yposition = 0;
1039 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1040 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1043 /* store index of each new playlist as a negative count, starting at -1 */
1045 if (pdz == playlist_dropzone_map.end()) {
1046 /* compute where this new track (which doesn't exist yet) will live
1049 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1051 /* How high is this region view ? */
1053 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1054 ArdourCanvas::Rect bbox;
1057 bbox = obbox.get ();
1060 last_track_bottom_edge += bbox.height();
1062 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1065 yposition = pdz->second;
1068 /* values are zero or negative, hence the use of min() */
1069 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1074 /* The TimeAxisView that this region is now over */
1075 TimeAxisView* current_tv = _time_axis_views[track_index];
1077 /* Ensure it is moved from stacked -> expanded if appropriate */
1078 if (current_tv->view()->layer_display() == Stacked) {
1079 current_tv->view()->set_layer_display (Expanded);
1082 /* We're only allowed to go -ve in layer on Expanded views */
1083 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1084 this_delta_layer = - i->layer;
1088 rv->set_height (current_tv->view()->child_height ());
1090 /* Update show/hidden status as the region view may have come from a hidden track,
1091 or have moved to one.
1093 if (current_tv->hidden ()) {
1094 rv->get_canvas_group()->hide ();
1096 rv->get_canvas_group()->show ();
1099 /* Update the DraggingView */
1100 i->time_axis_view = track_index;
1101 i->layer += this_delta_layer;
1104 _editor->mouse_brush_insert_region (rv, pending_region_position);
1108 /* Get the y coordinate of the top of the track that this region is now over */
1109 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1111 /* And adjust for the layer that it should be on */
1112 StreamView* cv = current_tv->view ();
1113 switch (cv->layer_display ()) {
1117 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1120 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1124 /* need to get the parent of the regionview
1125 * canvas group and get its position in
1126 * equivalent coordinate space as the trackview
1127 * we are now dragging over.
1130 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1135 /* Now move the region view */
1136 rv->move (x_delta, y_delta);
1138 } /* foreach region */
1140 _total_x_delta += x_delta;
1142 if (x_delta != 0 && !_brushing) {
1143 show_verbose_cursor_time (_last_frame_position);
1146 /* keep track of pointer movement */
1148 /* the pointer is currently over a time axis view */
1150 if (_last_pointer_time_axis_view < 0) {
1151 /* last motion event was not over a time axis view
1152 * or last y-movement out of the dropzone was not valid
1155 if (delta_time_axis_view < 0) {
1156 /* in the drop zone, moving up */
1158 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1159 * We do not use negative _last_pointer_time_axis_view because
1160 * the dropzone is "packed" (the actual track offset is ignored)
1162 * As opposed to the actual number
1163 * of elements in the dropzone (_ndropzone)
1164 * _pdropzone is not constrained. This is necessary
1165 * to allow moving multiple regions with y-distance
1168 * There can be 0 elements in the dropzone,
1169 * even though the drag-pointer is inside the DZ.
1172 * [ Audio-track, Midi-track, Audio-track, DZ ]
1173 * move regions from both audio tracks at the same time into the
1174 * DZ by grabbing the region in the bottom track.
1176 assert(current_pointer_time_axis_view >= 0);
1177 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1181 /* only move out of the zone if the movement is OK */
1182 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1183 assert(delta_time_axis_view < 0);
1184 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1185 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1186 * the current position can be calculated as follows:
1188 // a well placed oofus attack can still throw this off.
1189 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1190 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1193 /* last motion event was also over a time axis view */
1194 _last_pointer_time_axis_view += delta_time_axis_view;
1195 assert(_last_pointer_time_axis_view >= 0);
1200 /* the pointer is not over a time axis view */
1201 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1202 _pdropzone += delta_time_axis_view - delta_skip;
1203 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1206 _last_pointer_layer += delta_layer;
1210 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1212 if (_copy && first_move) {
1214 if (_x_constrained) {
1215 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1217 _editor->begin_reversible_command (Operations::region_copy);
1220 /* duplicate the regionview(s) and region(s) */
1222 list<DraggingView> new_regionviews;
1224 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1226 RegionView* rv = i->view;
1227 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1228 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1230 const boost::shared_ptr<const Region> original = rv->region();
1231 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1232 region_copy->set_position (original->position());
1233 /* need to set this so that the drop zone code can work. This doesn't
1234 actually put the region into the playlist, but just sets a weak pointer
1237 region_copy->set_playlist (original->playlist());
1241 boost::shared_ptr<AudioRegion> audioregion_copy
1242 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1244 nrv = new AudioRegionView (*arv, audioregion_copy);
1246 boost::shared_ptr<MidiRegion> midiregion_copy
1247 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1248 nrv = new MidiRegionView (*mrv, midiregion_copy);
1253 nrv->get_canvas_group()->show ();
1254 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1256 /* swap _primary to the copy */
1258 if (rv == _primary) {
1262 /* ..and deselect the one we copied */
1264 rv->set_selected (false);
1267 if (!new_regionviews.empty()) {
1269 /* reflect the fact that we are dragging the copies */
1271 _views = new_regionviews;
1273 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1276 } else if (!_copy && first_move) {
1278 if (_x_constrained) {
1279 _editor->begin_reversible_command (_("fixed time region drag"));
1281 _editor->begin_reversible_command (Operations::region_drag);
1285 RegionMotionDrag::motion (event, first_move);
1289 RegionMotionDrag::finished (GdkEvent *, bool)
1291 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1292 if (!(*i)->view()) {
1296 if ((*i)->view()->layer_display() == Expanded) {
1297 (*i)->view()->set_layer_display (Stacked);
1303 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1305 RegionMotionDrag::finished (ev, movement_occurred);
1307 if (!movement_occurred) {
1311 if (was_double_click() && !_views.empty()) {
1312 DraggingView dv = _views.front();
1313 dv.view->show_region_editor ();
1320 /* reverse this here so that we have the correct logic to finalize
1324 if (Config->get_edit_mode() == Lock) {
1325 _x_constrained = !_x_constrained;
1328 assert (!_views.empty ());
1330 /* We might have hidden region views so that they weren't visible during the drag
1331 (when they have been reparented). Now everything can be shown again, as region
1332 views are back in their track parent groups.
1334 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1335 i->view->get_canvas_group()->show ();
1338 bool const changed_position = (_last_frame_position != _primary->region()->position());
1339 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1340 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1360 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1364 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1366 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1371 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1372 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1373 uint32_t output_chan = region->n_channels();
1374 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1375 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1377 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name());
1378 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1380 rtav->set_height (original->current_height());
1384 ChanCount one_midi_port (DataType::MIDI, 1);
1385 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1386 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1387 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1389 rtav->set_height (original->current_height());
1394 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1400 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1402 RegionSelection new_views;
1403 PlaylistSet modified_playlists;
1404 RouteTimeAxisView* new_time_axis_view = 0;
1407 /* all changes were made during motion event handlers */
1409 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1413 _editor->commit_reversible_command ();
1417 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1418 PlaylistMapping playlist_mapping;
1420 /* insert the regions into their new playlists */
1421 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1423 RouteTimeAxisView* dest_rtv = 0;
1425 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1431 if (changed_position && !_x_constrained) {
1432 where = i->view->region()->position() - drag_delta;
1434 where = i->view->region()->position();
1437 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1438 /* dragged to drop zone */
1440 PlaylistMapping::iterator pm;
1442 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1443 /* first region from this original playlist: create a new track */
1444 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1445 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1446 dest_rtv = new_time_axis_view;
1448 /* we already created a new track for regions from this playlist, use it */
1449 dest_rtv = pm->second;
1452 /* destination time axis view is the one we dragged to */
1453 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1456 if (dest_rtv != 0) {
1457 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1458 if (new_view != 0) {
1459 new_views.push_back (new_view);
1463 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1464 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1467 list<DraggingView>::const_iterator next = i;
1473 /* If we've created new regions either by copying or moving
1474 to a new track, we want to replace the old selection with the new ones
1477 if (new_views.size() > 0) {
1478 _editor->selection->set (new_views);
1481 /* write commands for the accumulated diffs for all our modified playlists */
1482 add_stateful_diff_commands_for_playlists (modified_playlists);
1484 _editor->commit_reversible_command ();
1488 RegionMoveDrag::finished_no_copy (
1489 bool const changed_position,
1490 bool const changed_tracks,
1491 framecnt_t const drag_delta
1494 RegionSelection new_views;
1495 PlaylistSet modified_playlists;
1496 PlaylistSet frozen_playlists;
1497 set<RouteTimeAxisView*> views_to_update;
1498 RouteTimeAxisView* new_time_axis_view = 0;
1501 /* all changes were made during motion event handlers */
1502 _editor->commit_reversible_command ();
1506 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1507 PlaylistMapping playlist_mapping;
1509 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1511 RegionView* rv = i->view;
1512 RouteTimeAxisView* dest_rtv = 0;
1514 if (rv->region()->locked() || rv->region()->video_locked()) {
1519 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1520 /* dragged to drop zone */
1522 PlaylistMapping::iterator pm;
1524 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1525 /* first region from this original playlist: create a new track */
1526 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1527 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1528 dest_rtv = new_time_axis_view;
1530 /* we already created a new track for regions from this playlist, use it */
1531 dest_rtv = pm->second;
1535 /* destination time axis view is the one we dragged to */
1536 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1541 double const dest_layer = i->layer;
1543 views_to_update.insert (dest_rtv);
1547 if (changed_position && !_x_constrained) {
1548 where = rv->region()->position() - drag_delta;
1550 where = rv->region()->position();
1553 if (changed_tracks) {
1555 /* insert into new playlist */
1557 RegionView* new_view = insert_region_into_playlist (
1558 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1561 if (new_view == 0) {
1566 new_views.push_back (new_view);
1568 /* remove from old playlist */
1570 /* the region that used to be in the old playlist is not
1571 moved to the new one - we use a copy of it. as a result,
1572 any existing editor for the region should no longer be
1575 rv->hide_region_editor();
1578 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1582 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1584 /* this movement may result in a crossfade being modified, or a layering change,
1585 so we need to get undo data from the playlist as well as the region.
1588 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1590 playlist->clear_changes ();
1593 rv->region()->clear_changes ();
1596 motion on the same track. plonk the previously reparented region
1597 back to its original canvas group (its streamview).
1598 No need to do anything for copies as they are fake regions which will be deleted.
1601 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1602 rv->get_canvas_group()->set_y_position (i->initial_y);
1605 /* just change the model */
1606 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1607 playlist->set_layer (rv->region(), dest_layer);
1610 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1612 r = frozen_playlists.insert (playlist);
1615 playlist->freeze ();
1618 rv->region()->set_position (where);
1620 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1623 if (changed_tracks) {
1625 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1626 was selected in all of them, then removing it from a playlist will have removed all
1627 trace of it from _views (i.e. there were N regions selected, we removed 1,
1628 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1629 corresponding regionview, and _views is now empty).
1631 This could have invalidated any and all iterators into _views.
1633 The heuristic we use here is: if the region selection is empty, break out of the loop
1634 here. if the region selection is not empty, then restart the loop because we know that
1635 we must have removed at least the region(view) we've just been working on as well as any
1636 that we processed on previous iterations.
1638 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1639 we can just iterate.
1643 if (_views.empty()) {
1654 /* If we've created new regions either by copying or moving
1655 to a new track, we want to replace the old selection with the new ones
1658 if (new_views.size() > 0) {
1659 _editor->selection->set (new_views);
1662 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1666 /* write commands for the accumulated diffs for all our modified playlists */
1667 add_stateful_diff_commands_for_playlists (modified_playlists);
1669 _editor->commit_reversible_command ();
1671 /* We have futzed with the layering of canvas items on our streamviews.
1672 If any region changed layer, this will have resulted in the stream
1673 views being asked to set up their region views, and all will be well.
1674 If not, we might now have badly-ordered region views. Ask the StreamViews
1675 involved to sort themselves out, just in case.
1678 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1679 (*i)->view()->playlist_layered ((*i)->track ());
1683 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1684 * @param region Region to remove.
1685 * @param playlist playlist To remove from.
1686 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1687 * that clear_changes () is only called once per playlist.
1690 RegionMoveDrag::remove_region_from_playlist (
1691 boost::shared_ptr<Region> region,
1692 boost::shared_ptr<Playlist> playlist,
1693 PlaylistSet& modified_playlists
1696 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1699 playlist->clear_changes ();
1702 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1706 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1707 * clearing the playlist's diff history first if necessary.
1708 * @param region Region to insert.
1709 * @param dest_rtv Destination RouteTimeAxisView.
1710 * @param dest_layer Destination layer.
1711 * @param where Destination position.
1712 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1713 * that clear_changes () is only called once per playlist.
1714 * @return New RegionView, or 0 if no insert was performed.
1717 RegionMoveDrag::insert_region_into_playlist (
1718 boost::shared_ptr<Region> region,
1719 RouteTimeAxisView* dest_rtv,
1722 PlaylistSet& modified_playlists
1725 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1726 if (!dest_playlist) {
1730 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1731 _new_region_view = 0;
1732 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1734 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1735 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1737 dest_playlist->clear_changes ();
1740 dest_playlist->add_region (region, where);
1742 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1743 dest_playlist->set_layer (region, dest_layer);
1748 assert (_new_region_view);
1750 return _new_region_view;
1754 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1756 _new_region_view = rv;
1760 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1762 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1763 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1765 _editor->session()->add_command (c);
1774 RegionMoveDrag::aborted (bool movement_occurred)
1778 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1779 list<DraggingView>::const_iterator next = i;
1788 RegionMotionDrag::aborted (movement_occurred);
1793 RegionMotionDrag::aborted (bool)
1795 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1797 StreamView* sview = (*i)->view();
1800 if (sview->layer_display() == Expanded) {
1801 sview->set_layer_display (Stacked);
1806 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1807 RegionView* rv = i->view;
1808 TimeAxisView* tv = &(rv->get_time_axis_view ());
1809 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1811 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1812 rv->get_canvas_group()->set_y_position (0);
1814 rv->move (-_total_x_delta, 0);
1815 rv->set_height (rtv->view()->child_height ());
1819 /** @param b true to brush, otherwise false.
1820 * @param c true to make copies of the regions being moved, otherwise false.
1822 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1823 : RegionMotionDrag (e, i, p, v, b)
1826 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1829 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1830 if (rtv && rtv->is_track()) {
1831 speed = rtv->track()->speed ();
1834 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1838 RegionMoveDrag::setup_pointer_frame_offset ()
1840 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1843 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1844 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1846 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1848 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1849 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1851 _primary = v->view()->create_region_view (r, false, false);
1853 _primary->get_canvas_group()->show ();
1854 _primary->set_position (pos, 0);
1855 _views.push_back (DraggingView (_primary, this, v));
1857 _last_frame_position = pos;
1859 _item = _primary->get_canvas_group ();
1863 RegionInsertDrag::finished (GdkEvent *, bool)
1865 int pos = _views.front().time_axis_view;
1866 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1868 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1870 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1871 _primary->get_canvas_group()->set_y_position (0);
1873 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1875 _editor->begin_reversible_command (Operations::insert_region);
1876 playlist->clear_changes ();
1877 playlist->add_region (_primary->region (), _last_frame_position);
1879 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1880 if (Config->get_edit_mode() == Ripple) {
1881 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1884 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1885 _editor->commit_reversible_command ();
1893 RegionInsertDrag::aborted (bool)
1900 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1901 : RegionMoveDrag (e, i, p, v, false, false)
1903 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1906 struct RegionSelectionByPosition {
1907 bool operator() (RegionView*a, RegionView* b) {
1908 return a->region()->position () < b->region()->position();
1913 RegionSpliceDrag::motion (GdkEvent* event, bool)
1915 /* Which trackview is this ? */
1917 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1918 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1920 /* The region motion is only processed if the pointer is over
1924 if (!tv || !tv->is_track()) {
1925 /* To make sure we hide the verbose canvas cursor when the mouse is
1926 not held over an audio track.
1928 _editor->verbose_cursor()->hide ();
1931 _editor->verbose_cursor()->show ();
1936 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1942 RegionSelection copy;
1943 _editor->selection->regions.by_position(copy);
1945 framepos_t const pf = adjusted_current_frame (event);
1947 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1949 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1955 boost::shared_ptr<Playlist> playlist;
1957 if ((playlist = atv->playlist()) == 0) {
1961 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1966 if (pf < (*i)->region()->last_frame() + 1) {
1970 if (pf > (*i)->region()->first_frame()) {
1976 playlist->shuffle ((*i)->region(), dir);
1981 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1983 RegionMoveDrag::finished (event, movement_occurred);
1987 RegionSpliceDrag::aborted (bool)
1997 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2000 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2002 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2003 RegionSelection to_ripple;
2004 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2005 if ((*i)->position() >= where) {
2006 to_ripple.push_back (rtv->view()->find_view(*i));
2010 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2011 if (!exclude.contains (*i)) {
2012 // the selection has already been added to _views
2014 if (drag_in_progress) {
2015 // do the same things that RegionMotionDrag::motion does when
2016 // first_move is true, for the region views that we're adding
2017 // to _views this time
2020 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2021 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2022 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2023 rvg->reparent (_editor->_drag_motion_group);
2025 // we only need to move in the y direction
2026 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2031 _views.push_back (DraggingView (*i, this, tav));
2037 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2040 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2041 // we added all the regions after the selection
2043 std::list<DraggingView>::iterator to_erase = i++;
2044 if (!_editor->selection->regions.contains (to_erase->view)) {
2045 // restore the non-selected regions to their original playlist & positions,
2046 // and then ripple them back by the length of the regions that were dragged away
2047 // do the same things as RegionMotionDrag::aborted
2049 RegionView *rv = to_erase->view;
2050 TimeAxisView* tv = &(rv->get_time_axis_view ());
2051 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2054 // plonk them back onto their own track
2055 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2056 rv->get_canvas_group()->set_y_position (0);
2060 // move the underlying region to match the view
2061 rv->region()->set_position (rv->region()->position() + amount);
2063 // restore the view to match the underlying region's original position
2064 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2067 rv->set_height (rtv->view()->child_height ());
2068 _views.erase (to_erase);
2074 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2076 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2078 return allow_moves_across_tracks;
2086 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2087 : RegionMoveDrag (e, i, p, v, false, false)
2089 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2090 // compute length of selection
2091 RegionSelection selected_regions = _editor->selection->regions;
2092 selection_length = selected_regions.end_frame() - selected_regions.start();
2094 // we'll only allow dragging to another track in ripple mode if all the regions
2095 // being dragged start off on the same track
2096 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2099 exclude = new RegionList;
2100 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2101 exclude->push_back((*i)->region());
2104 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2105 RegionSelection copy;
2106 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2108 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2109 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2111 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2112 // find ripple start point on each applicable playlist
2113 RegionView *first_selected_on_this_track = NULL;
2114 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2115 if ((*i)->region()->playlist() == (*pi)) {
2116 // region is on this playlist - it's the first, because they're sorted
2117 first_selected_on_this_track = *i;
2121 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2122 add_all_after_to_views (
2123 &first_selected_on_this_track->get_time_axis_view(),
2124 first_selected_on_this_track->region()->position(),
2125 selected_regions, false);
2128 if (allow_moves_across_tracks) {
2129 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2137 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2139 /* Which trackview is this ? */
2141 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2142 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2144 /* The region motion is only processed if the pointer is over
2148 if (!tv || !tv->is_track()) {
2149 /* To make sure we hide the verbose canvas cursor when the mouse is
2150 not held over an audiotrack.
2152 _editor->verbose_cursor()->hide ();
2156 framepos_t where = adjusted_current_frame (event);
2157 assert (where >= 0);
2159 double delta = compute_x_delta (event, &after);
2161 framecnt_t amount = _editor->pixel_to_sample (delta);
2163 if (allow_moves_across_tracks) {
2164 // all the originally selected regions were on the same track
2166 framecnt_t adjust = 0;
2167 if (prev_tav && tv != prev_tav) {
2168 // dragged onto a different track
2169 // remove the unselected regions from _views, restore them to their original positions
2170 // and add the regions after the drop point on the new playlist to _views instead.
2171 // undo the effect of rippling the previous playlist, and include the effect of removing
2172 // the dragged region(s) from this track
2174 remove_unselected_from_views (prev_amount, false);
2175 // ripple previous playlist according to the regions that have been removed onto the new playlist
2176 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2179 // move just the selected regions
2180 RegionMoveDrag::motion(event, first_move);
2182 // ensure that the ripple operation on the new playlist inserts selection_length time
2183 adjust = selection_length;
2184 // ripple the new current playlist
2185 tv->playlist()->ripple (where, amount+adjust, exclude);
2187 // add regions after point where drag entered this track to subsequent ripples
2188 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2191 // motion on same track
2192 RegionMoveDrag::motion(event, first_move);
2196 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2197 prev_position = where;
2199 // selection encompasses multiple tracks - just drag
2200 // cross-track drags are forbidden
2201 RegionMoveDrag::motion(event, first_move);
2204 if (!_x_constrained) {
2205 prev_amount += amount;
2208 _last_frame_position = after;
2212 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2214 if (!movement_occurred) {
2218 if (was_double_click() && !_views.empty()) {
2219 DraggingView dv = _views.front();
2220 dv.view->show_region_editor ();
2227 _editor->begin_reversible_command(_("Ripple drag"));
2229 // remove the regions being rippled from the dragging view, updating them to
2230 // their new positions
2231 remove_unselected_from_views (prev_amount, true);
2233 if (allow_moves_across_tracks) {
2235 // if regions were dragged across tracks, we've rippled any later
2236 // regions on the track the regions were dragged off, so we need
2237 // to add the original track to the undo record
2238 orig_tav->playlist()->clear_changes();
2239 vector<Command*> cmds;
2240 orig_tav->playlist()->rdiff (cmds);
2241 _editor->session()->add_commands (cmds);
2243 if (prev_tav && prev_tav != orig_tav) {
2244 prev_tav->playlist()->clear_changes();
2245 vector<Command*> cmds;
2246 prev_tav->playlist()->rdiff (cmds);
2247 _editor->session()->add_commands (cmds);
2250 // selection spanned multiple tracks - all will need adding to undo record
2252 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2253 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2255 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2256 (*pi)->clear_changes();
2257 vector<Command*> cmds;
2258 (*pi)->rdiff (cmds);
2259 _editor->session()->add_commands (cmds);
2263 // other modified playlists are added to undo by RegionMoveDrag::finished()
2264 RegionMoveDrag::finished (event, movement_occurred);
2265 _editor->commit_reversible_command();
2269 RegionRippleDrag::aborted (bool movement_occurred)
2271 RegionMoveDrag::aborted (movement_occurred);
2276 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2278 _view (dynamic_cast<MidiTimeAxisView*> (v))
2280 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2286 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2289 _region = add_midi_region (_view);
2290 _view->playlist()->freeze ();
2293 framepos_t const f = adjusted_current_frame (event);
2294 if (f < grab_frame()) {
2295 _region->set_position (f);
2298 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2299 so that if this region is duplicated, its duplicate starts on
2300 a snap point rather than 1 frame after a snap point. Otherwise things get
2301 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2302 place snapped notes at the start of the region.
2305 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2306 _region->set_length (len < 1 ? 1 : len);
2312 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2314 if (!movement_occurred) {
2315 add_midi_region (_view);
2317 _view->playlist()->thaw ();
2322 RegionCreateDrag::aborted (bool)
2325 _view->playlist()->thaw ();
2331 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2336 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2340 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2342 Gdk::Cursor* cursor;
2343 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2345 float x_fraction = cnote->mouse_x_fraction ();
2347 if (x_fraction > 0.0 && x_fraction < 0.25) {
2348 cursor = _editor->cursors()->left_side_trim;
2351 cursor = _editor->cursors()->right_side_trim;
2355 Drag::start_grab (event, cursor);
2357 region = &cnote->region_view();
2359 if (_editor->snap_delta () == SnapRelative) {
2361 temp = region->snap_to_pixel_no_magnets (cnote->x0 ());
2362 _snap_delta = temp - cnote->x0 ();
2367 if (event->motion.state & Keyboard::PrimaryModifier) {
2373 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2375 if (ms.size() > 1) {
2376 /* has to be relative, may make no sense otherwise */
2380 /* select this note; if it is already selected, preserve the existing selection,
2381 otherwise make this note the only one selected.
2383 region->note_selected (cnote, cnote->selected ());
2385 _editor->begin_reversible_command (_("resize notes"));
2387 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2388 MidiRegionSelection::iterator next;
2391 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2393 mrv->begin_resizing (at_front);
2400 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
2402 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2403 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2404 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2406 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2408 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, _snap_delta);
2414 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
2416 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2417 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2418 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2420 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2422 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, _snap_delta);
2426 _editor->commit_reversible_command ();
2430 NoteResizeDrag::aborted (bool)
2432 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2433 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2434 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2436 mrv->abort_resizing ();
2441 AVDraggingView::AVDraggingView (RegionView* v)
2444 initial_position = v->region()->position ();
2447 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2450 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2453 TrackViewList empty;
2455 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2456 std::list<RegionView*> views = rs.by_layer();
2458 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2459 RegionView* rv = (*i);
2460 if (!rv->region()->video_locked()) {
2463 _views.push_back (AVDraggingView (rv));
2468 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2470 Drag::start_grab (event);
2471 if (_editor->session() == 0) {
2475 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2476 _max_backwards_drag = (
2477 ARDOUR_UI::instance()->video_timeline->get_duration()
2478 + ARDOUR_UI::instance()->video_timeline->get_offset()
2479 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2482 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2483 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2484 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2487 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2490 Timecode::Time timecode;
2491 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2492 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);
2493 show_verbose_cursor_text (buf);
2497 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2499 if (_editor->session() == 0) {
2502 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2506 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2507 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2509 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2510 dt = - _max_backwards_drag;
2513 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2514 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2516 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2517 RegionView* rv = i->view;
2518 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2521 rv->region()->clear_changes ();
2522 rv->region()->suspend_property_changes();
2524 rv->region()->set_position(i->initial_position + dt);
2525 rv->region_changed(ARDOUR::Properties::position);
2528 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2529 Timecode::Time timecode;
2530 Timecode::Time timediff;
2532 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2533 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2534 snprintf (buf, sizeof (buf),
2535 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2536 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2537 , _("Video Start:"),
2538 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2540 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2542 show_verbose_cursor_text (buf);
2546 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2548 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2552 if (!movement_occurred || ! _editor->session()) {
2556 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2558 _editor->begin_reversible_command (_("Move Video"));
2560 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2561 ARDOUR_UI::instance()->video_timeline->save_undo();
2562 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2563 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2565 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2566 i->view->drag_end();
2567 i->view->region()->resume_property_changes ();
2569 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2572 _editor->session()->maybe_update_session_range(
2573 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2574 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2578 _editor->commit_reversible_command ();
2582 VideoTimeLineDrag::aborted (bool)
2584 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2587 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2588 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2590 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2591 i->view->region()->resume_property_changes ();
2592 i->view->region()->set_position(i->initial_position);
2596 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2597 : RegionDrag (e, i, p, v)
2598 , _preserve_fade_anchor (preserve_fade_anchor)
2599 , _jump_position_when_done (false)
2601 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2605 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2608 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2609 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2611 if (tv && tv->is_track()) {
2612 speed = tv->track()->speed();
2615 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2616 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2617 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2619 framepos_t const pf = adjusted_current_frame (event);
2620 setup_snap_delta (region_start);
2622 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2623 /* Move the contents of the region around without changing the region bounds */
2624 _operation = ContentsTrim;
2625 Drag::start_grab (event, _editor->cursors()->trimmer);
2627 /* These will get overridden for a point trim.*/
2628 if (pf < (region_start + region_length/2)) {
2629 /* closer to front */
2630 _operation = StartTrim;
2632 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2633 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2635 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2639 _operation = EndTrim;
2640 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2641 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2643 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2648 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2649 _jump_position_when_done = true;
2652 switch (_operation) {
2654 show_verbose_cursor_time (region_start);
2655 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2656 i->view->trim_front_starting ();
2660 show_verbose_cursor_duration (region_start, region_end);
2663 show_verbose_cursor_time (pf);
2667 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2668 i->view->region()->suspend_property_changes ();
2673 TrimDrag::motion (GdkEvent* event, bool first_move)
2675 RegionView* rv = _primary;
2678 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2679 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2680 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2681 frameoffset_t frame_delta = 0;
2683 if (tv && tv->is_track()) {
2684 speed = tv->track()->speed();
2686 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (), event, true);
2687 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta ();
2693 switch (_operation) {
2695 trim_type = "Region start trim";
2698 trim_type = "Region end trim";
2701 trim_type = "Region content trim";
2708 _editor->begin_reversible_command (trim_type);
2710 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2711 RegionView* rv = i->view;
2712 rv->enable_display (false);
2713 rv->region()->playlist()->clear_owned_changes ();
2715 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2718 arv->temporarily_hide_envelope ();
2722 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2723 insert_result = _editor->motion_frozen_playlists.insert (pl);
2725 if (insert_result.second) {
2731 bool non_overlap_trim = false;
2733 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2734 non_overlap_trim = true;
2737 /* contstrain trim to fade length */
2738 if (_preserve_fade_anchor) {
2739 switch (_operation) {
2741 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2742 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2744 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2745 if (ar->locked()) continue;
2746 framecnt_t len = ar->fade_in()->back()->when;
2747 if (len < dt) dt = min(dt, len);
2751 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2752 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2754 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2755 if (ar->locked()) continue;
2756 framecnt_t len = ar->fade_out()->back()->when;
2757 if (len < -dt) dt = max(dt, -len);
2766 switch (_operation) {
2768 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2769 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2770 if (changed && _preserve_fade_anchor) {
2771 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2773 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2774 framecnt_t len = ar->fade_in()->back()->when;
2775 framecnt_t diff = ar->first_frame() - i->initial_position;
2776 framepos_t new_length = len - diff;
2777 i->anchored_fade_length = min (ar->length(), new_length);
2778 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2779 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2786 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2787 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2788 if (changed && _preserve_fade_anchor) {
2789 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2791 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2792 framecnt_t len = ar->fade_out()->back()->when;
2793 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2794 framepos_t new_length = len + diff;
2795 i->anchored_fade_length = min (ar->length(), new_length);
2796 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2797 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2805 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2807 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2808 i->view->move_contents (frame_delta);
2814 switch (_operation) {
2816 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2819 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2822 // show_verbose_cursor_time (frame_delta);
2829 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2831 if (movement_occurred) {
2832 motion (event, false);
2834 if (_operation == StartTrim) {
2835 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2837 /* This must happen before the region's StatefulDiffCommand is created, as it may
2838 `correct' (ahem) the region's _start from being negative to being zero. It
2839 needs to be zero in the undo record.
2841 i->view->trim_front_ending ();
2843 if (_preserve_fade_anchor) {
2844 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2846 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2847 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2848 ar->set_fade_in_length(i->anchored_fade_length);
2849 ar->set_fade_in_active(true);
2852 if (_jump_position_when_done) {
2853 i->view->region()->set_position (i->initial_position);
2856 } else if (_operation == EndTrim) {
2857 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2858 if (_preserve_fade_anchor) {
2859 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2861 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2862 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2863 ar->set_fade_out_length(i->anchored_fade_length);
2864 ar->set_fade_out_active(true);
2867 if (_jump_position_when_done) {
2868 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2873 if (!_views.empty()) {
2874 if (_operation == StartTrim) {
2875 _editor->maybe_locate_with_edit_preroll(
2876 _views.begin()->view->region()->position());
2878 if (_operation == EndTrim) {
2879 _editor->maybe_locate_with_edit_preroll(
2880 _views.begin()->view->region()->position() +
2881 _views.begin()->view->region()->length());
2885 if (!_editor->selection->selected (_primary)) {
2886 _primary->thaw_after_trim ();
2889 set<boost::shared_ptr<Playlist> > diffed_playlists;
2891 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2892 i->view->thaw_after_trim ();
2893 i->view->enable_display (true);
2895 /* Trimming one region may affect others on the playlist, so we need
2896 to get undo Commands from the whole playlist rather than just the
2897 region. Use diffed_playlists to make sure we don't diff a given
2898 playlist more than once.
2900 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2901 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2902 vector<Command*> cmds;
2904 _editor->session()->add_commands (cmds);
2905 diffed_playlists.insert (p);
2910 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2914 _editor->motion_frozen_playlists.clear ();
2915 _editor->commit_reversible_command();
2918 /* no mouse movement */
2919 _editor->point_trim (event, adjusted_current_frame (event));
2922 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2923 if (_operation == StartTrim) {
2924 i->view->trim_front_ending ();
2927 i->view->region()->resume_property_changes ();
2932 TrimDrag::aborted (bool movement_occurred)
2934 /* Our motion method is changing model state, so use the Undo system
2935 to cancel. Perhaps not ideal, as this will leave an Undo point
2936 behind which may be slightly odd from the user's point of view.
2941 if (movement_occurred) {
2945 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2946 i->view->region()->resume_property_changes ();
2951 TrimDrag::setup_pointer_frame_offset ()
2953 list<DraggingView>::iterator i = _views.begin ();
2954 while (i != _views.end() && i->view != _primary) {
2958 if (i == _views.end()) {
2962 switch (_operation) {
2964 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2967 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2974 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2978 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2979 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2984 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2986 Drag::start_grab (event, cursor);
2987 show_verbose_cursor_time (adjusted_current_frame(event));
2991 MeterMarkerDrag::setup_pointer_frame_offset ()
2993 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2997 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2999 if (!_marker->meter().movable()) {
3005 // create a dummy marker for visual representation of moving the
3006 // section, because whether its a copy or not, we're going to
3007 // leave or lose the original marker (leave if its a copy; lose if its
3008 // not, because we'll remove it from the map).
3010 MeterSection section (_marker->meter());
3012 if (!section.movable()) {
3017 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3019 _marker = new MeterMarker (
3021 *_editor->meter_group,
3022 ARDOUR_UI::config()->color ("meter marker"),
3024 *new MeterSection (_marker->meter())
3027 /* use the new marker for the grab */
3028 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3031 TempoMap& map (_editor->session()->tempo_map());
3032 /* get current state */
3033 before_state = &map.get_state();
3034 /* remove the section while we drag it */
3035 map.remove_meter (section, true);
3039 framepos_t const pf = adjusted_current_frame (event);
3041 _marker->set_position (pf);
3042 show_verbose_cursor_time (pf);
3046 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3048 if (!movement_occurred) {
3049 if (was_double_click()) {
3050 _editor->edit_meter_marker (*_marker);
3055 if (!_marker->meter().movable()) {
3059 motion (event, false);
3061 Timecode::BBT_Time when;
3063 TempoMap& map (_editor->session()->tempo_map());
3064 map.bbt_time (last_pointer_frame(), when);
3066 if (_copy == true) {
3067 _editor->begin_reversible_command (_("copy meter mark"));
3068 XMLNode &before = map.get_state();
3069 map.add_meter (_marker->meter(), when);
3070 XMLNode &after = map.get_state();
3071 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3072 _editor->commit_reversible_command ();
3075 _editor->begin_reversible_command (_("move meter mark"));
3077 /* we removed it before, so add it back now */
3079 map.add_meter (_marker->meter(), when);
3080 XMLNode &after = map.get_state();
3081 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3082 _editor->commit_reversible_command ();
3085 // delete the dummy marker we used for visual representation while moving.
3086 // a new visual marker will show up automatically.
3091 MeterMarkerDrag::aborted (bool moved)
3093 _marker->set_position (_marker->meter().frame ());
3096 TempoMap& map (_editor->session()->tempo_map());
3097 /* we removed it before, so add it back now */
3098 map.add_meter (_marker->meter(), _marker->meter().frame());
3099 // delete the dummy marker we used for visual representation while moving.
3100 // a new visual marker will show up automatically.
3105 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3109 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3111 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3116 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3118 Drag::start_grab (event, cursor);
3119 show_verbose_cursor_time (adjusted_current_frame (event));
3123 TempoMarkerDrag::setup_pointer_frame_offset ()
3125 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3129 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3131 if (!_marker->tempo().movable()) {
3137 // create a dummy marker for visual representation of moving the
3138 // section, because whether its a copy or not, we're going to
3139 // leave or lose the original marker (leave if its a copy; lose if its
3140 // not, because we'll remove it from the map).
3142 // create a dummy marker for visual representation of moving the copy.
3143 // The actual copying is not done before we reach the finish callback.
3146 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3148 TempoSection section (_marker->tempo());
3150 _marker = new TempoMarker (
3152 *_editor->tempo_group,
3153 ARDOUR_UI::config()->color ("tempo marker"),
3155 *new TempoSection (_marker->tempo())
3158 /* use the new marker for the grab */
3159 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3162 TempoMap& map (_editor->session()->tempo_map());
3163 /* get current state */
3164 before_state = &map.get_state();
3165 /* remove the section while we drag it */
3166 map.remove_tempo (section, true);
3170 framepos_t const pf = adjusted_current_frame (event);
3171 _marker->set_position (pf);
3172 show_verbose_cursor_time (pf);
3176 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3178 if (!movement_occurred) {
3179 if (was_double_click()) {
3180 _editor->edit_tempo_marker (*_marker);
3185 if (!_marker->tempo().movable()) {
3189 motion (event, false);
3191 TempoMap& map (_editor->session()->tempo_map());
3192 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3193 Timecode::BBT_Time when;
3195 map.bbt_time (beat_time, when);
3197 if (_copy == true) {
3198 _editor->begin_reversible_command (_("copy tempo mark"));
3199 XMLNode &before = map.get_state();
3200 map.add_tempo (_marker->tempo(), when);
3201 XMLNode &after = map.get_state();
3202 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3203 _editor->commit_reversible_command ();
3206 _editor->begin_reversible_command (_("move tempo mark"));
3207 /* we removed it before, so add it back now */
3208 map.add_tempo (_marker->tempo(), when);
3209 XMLNode &after = map.get_state();
3210 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3211 _editor->commit_reversible_command ();
3214 // delete the dummy marker we used for visual representation while moving.
3215 // a new visual marker will show up automatically.
3220 TempoMarkerDrag::aborted (bool moved)
3222 _marker->set_position (_marker->tempo().frame());
3224 TempoMap& map (_editor->session()->tempo_map());
3225 /* we removed it before, so add it back now */
3226 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3227 // delete the dummy marker we used for visual representation while moving.
3228 // a new visual marker will show up automatically.
3233 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3234 : Drag (e, &c.track_canvas_item(), false)
3238 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3241 /** Do all the things we do when dragging the playhead to make it look as though
3242 * we have located, without actually doing the locate (because that would cause
3243 * the diskstream buffers to be refilled, which is too slow).
3246 CursorDrag::fake_locate (framepos_t t)
3248 _editor->playhead_cursor->set_position (t);
3250 Session* s = _editor->session ();
3251 if (s->timecode_transmission_suspended ()) {
3252 framepos_t const f = _editor->playhead_cursor->current_frame ();
3253 /* This is asynchronous so it will be sent "now"
3255 s->send_mmc_locate (f);
3256 /* These are synchronous and will be sent during the next
3259 s->queue_full_time_code ();
3260 s->queue_song_position_pointer ();
3263 show_verbose_cursor_time (t);
3264 _editor->UpdateAllTransportClocks (t);
3268 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3270 Drag::start_grab (event, c);
3271 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3273 _grab_zoom = _editor->samples_per_pixel;
3275 framepos_t where = _editor->canvas_event_sample (event) + snap_delta ();
3277 _editor->snap_to_with_modifier (where, event);
3279 _editor->_dragging_playhead = true;
3281 Session* s = _editor->session ();
3283 /* grab the track canvas item as well */
3285 _cursor.track_canvas_item().grab();
3288 if (_was_rolling && _stop) {
3292 if (s->is_auditioning()) {
3293 s->cancel_audition ();
3297 if (AudioEngine::instance()->connected()) {
3299 /* do this only if we're the engine is connected
3300 * because otherwise this request will never be
3301 * serviced and we'll busy wait forever. likewise,
3302 * notice if we are disconnected while waiting for the
3303 * request to be serviced.
3306 s->request_suspend_timecode_transmission ();
3307 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3308 /* twiddle our thumbs */
3313 fake_locate (where - snap_delta ());
3317 CursorDrag::motion (GdkEvent* event, bool)
3319 framepos_t where = _editor->canvas_event_sample (event) + snap_delta ();
3320 _editor->snap_to_with_modifier (where, event);
3321 if (where != last_pointer_frame()) {
3322 fake_locate (where - snap_delta ());
3327 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3329 _editor->_dragging_playhead = false;
3331 _cursor.track_canvas_item().ungrab();
3333 if (!movement_occurred && _stop) {
3337 motion (event, false);
3339 Session* s = _editor->session ();
3341 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3342 _editor->_pending_locate_request = true;
3343 s->request_resume_timecode_transmission ();
3348 CursorDrag::aborted (bool)
3350 _cursor.track_canvas_item().ungrab();
3352 if (_editor->_dragging_playhead) {
3353 _editor->session()->request_resume_timecode_transmission ();
3354 _editor->_dragging_playhead = false;
3357 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3360 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3361 : RegionDrag (e, i, p, v)
3363 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3367 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3369 Drag::start_grab (event, cursor);
3371 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3372 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3373 setup_snap_delta (r->position ());
3375 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3379 FadeInDrag::setup_pointer_frame_offset ()
3381 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3382 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3383 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3387 FadeInDrag::motion (GdkEvent* event, bool)
3389 framecnt_t fade_length;
3391 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta ();
3392 _editor->snap_to_with_modifier (pos, event);
3393 pos -= snap_delta ();
3395 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3397 if (pos < (region->position() + 64)) {
3398 fade_length = 64; // this should be a minimum defined somewhere
3399 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3400 fade_length = region->length() - region->fade_out()->back()->when - 1;
3402 fade_length = pos - region->position();
3405 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3407 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3413 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3416 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3420 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3422 if (!movement_occurred) {
3426 framecnt_t fade_length;
3427 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta ();
3428 _editor->snap_to_with_modifier (pos, event);
3429 pos -= snap_delta ();
3431 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3433 if (pos < (region->position() + 64)) {
3434 fade_length = 64; // this should be a minimum defined somewhere
3435 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3436 fade_length = region->length() - region->fade_out()->back()->when - 1;
3438 fade_length = pos - region->position();
3441 _editor->begin_reversible_command (_("change fade in length"));
3443 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3445 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3451 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3452 XMLNode &before = alist->get_state();
3454 tmp->audio_region()->set_fade_in_length (fade_length);
3455 tmp->audio_region()->set_fade_in_active (true);
3457 XMLNode &after = alist->get_state();
3458 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3461 _editor->commit_reversible_command ();
3465 FadeInDrag::aborted (bool)
3467 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3468 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3474 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3478 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3479 : RegionDrag (e, i, p, v)
3481 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3485 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3487 Drag::start_grab (event, cursor);
3489 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3490 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3491 setup_snap_delta (r->last_frame ());
3493 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3497 FadeOutDrag::setup_pointer_frame_offset ()
3499 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3500 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3501 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3505 FadeOutDrag::motion (GdkEvent* event, bool)
3507 framecnt_t fade_length;
3509 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta ();
3510 _editor->snap_to_with_modifier (pos, event);
3511 pos -= snap_delta ();
3513 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3515 if (pos > (region->last_frame() - 64)) {
3516 fade_length = 64; // this should really be a minimum fade defined somewhere
3517 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3518 fade_length = region->length() - region->fade_in()->back()->when - 1;
3520 fade_length = region->last_frame() - pos;
3523 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3525 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3531 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3534 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3538 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3540 if (!movement_occurred) {
3544 framecnt_t fade_length;
3546 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta ();
3547 _editor->snap_to_with_modifier (pos, event);
3548 pos -= snap_delta ();
3550 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3552 if (pos > (region->last_frame() - 64)) {
3553 fade_length = 64; // this should really be a minimum fade defined somewhere
3554 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3555 fade_length = region->length() - region->fade_in()->back()->when - 1;
3557 fade_length = region->last_frame() - pos;
3560 _editor->begin_reversible_command (_("change fade out length"));
3562 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3564 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3570 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3571 XMLNode &before = alist->get_state();
3573 tmp->audio_region()->set_fade_out_length (fade_length);
3574 tmp->audio_region()->set_fade_out_active (true);
3576 XMLNode &after = alist->get_state();
3577 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3580 _editor->commit_reversible_command ();
3584 FadeOutDrag::aborted (bool)
3586 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3587 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3593 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3597 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3600 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3602 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3605 _points.push_back (ArdourCanvas::Duple (0, 0));
3606 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3609 MarkerDrag::~MarkerDrag ()
3611 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3616 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3618 location = new Location (*l);
3619 markers.push_back (m);
3624 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3626 Drag::start_grab (event, cursor);
3630 Location *location = _editor->find_location_from_marker (_marker, is_start);
3631 _editor->_dragging_edit_point = true;
3633 update_item (location);
3635 // _drag_line->show();
3636 // _line->raise_to_top();
3639 show_verbose_cursor_time (location->start());
3641 show_verbose_cursor_time (location->end());
3644 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3647 case Selection::Toggle:
3648 /* we toggle on the button release */
3650 case Selection::Set:
3651 if (!_editor->selection->selected (_marker)) {
3652 _editor->selection->set (_marker);
3655 case Selection::Extend:
3657 Locations::LocationList ll;
3658 list<Marker*> to_add;
3660 _editor->selection->markers.range (s, e);
3661 s = min (_marker->position(), s);
3662 e = max (_marker->position(), e);
3665 if (e < max_framepos) {
3668 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3669 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3670 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3673 to_add.push_back (lm->start);
3676 to_add.push_back (lm->end);
3680 if (!to_add.empty()) {
3681 _editor->selection->add (to_add);
3685 case Selection::Add:
3686 _editor->selection->add (_marker);
3690 /* Set up copies for us to manipulate during the drag
3693 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3695 Location* l = _editor->find_location_from_marker (*i, is_start);
3702 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3704 /* range: check that the other end of the range isn't
3707 CopiedLocationInfo::iterator x;
3708 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3709 if (*(*x).location == *l) {
3713 if (x == _copied_locations.end()) {
3714 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3716 (*x).markers.push_back (*i);
3717 (*x).move_both = true;
3725 MarkerDrag::setup_pointer_frame_offset ()
3728 Location *location = _editor->find_location_from_marker (_marker, is_start);
3729 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3733 MarkerDrag::motion (GdkEvent* event, bool)
3735 framecnt_t f_delta = 0;
3737 bool move_both = false;
3738 Location *real_location;
3739 Location *copy_location = 0;
3741 framepos_t const newframe = adjusted_current_frame (event);
3742 framepos_t next = newframe;
3744 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3748 CopiedLocationInfo::iterator x;
3750 /* find the marker we're dragging, and compute the delta */
3752 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3754 copy_location = (*x).location;
3756 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3758 /* this marker is represented by this
3759 * CopiedLocationMarkerInfo
3762 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3767 if (real_location->is_mark()) {
3768 f_delta = newframe - copy_location->start();
3772 switch (_marker->type()) {
3773 case Marker::SessionStart:
3774 case Marker::RangeStart:
3775 case Marker::LoopStart:
3776 case Marker::PunchIn:
3777 f_delta = newframe - copy_location->start();
3780 case Marker::SessionEnd:
3781 case Marker::RangeEnd:
3782 case Marker::LoopEnd:
3783 case Marker::PunchOut:
3784 f_delta = newframe - copy_location->end();
3787 /* what kind of marker is this ? */
3796 if (x == _copied_locations.end()) {
3797 /* hmm, impossible - we didn't find the dragged marker */
3801 /* now move them all */
3803 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3805 copy_location = x->location;
3807 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3811 if (real_location->locked()) {
3815 if (copy_location->is_mark()) {
3819 copy_location->set_start (copy_location->start() + f_delta);
3823 framepos_t new_start = copy_location->start() + f_delta;
3824 framepos_t new_end = copy_location->end() + f_delta;
3826 if (is_start) { // start-of-range marker
3828 if (move_both || (*x).move_both) {
3829 copy_location->set_start (new_start);
3830 copy_location->set_end (new_end);
3831 } else if (new_start < copy_location->end()) {
3832 copy_location->set_start (new_start);
3833 } else if (newframe > 0) {
3834 _editor->snap_to (next, RoundUpAlways, true);
3835 copy_location->set_end (next);
3836 copy_location->set_start (newframe);
3839 } else { // end marker
3841 if (move_both || (*x).move_both) {
3842 copy_location->set_end (new_end);
3843 copy_location->set_start (new_start);
3844 } else if (new_end > copy_location->start()) {
3845 copy_location->set_end (new_end);
3846 } else if (newframe > 0) {
3847 _editor->snap_to (next, RoundDownAlways, true);
3848 copy_location->set_start (next);
3849 copy_location->set_end (newframe);
3854 update_item (copy_location);
3856 /* now lookup the actual GUI items used to display this
3857 * location and move them to wherever the copy of the location
3858 * is now. This means that the logic in ARDOUR::Location is
3859 * still enforced, even though we are not (yet) modifying
3860 * the real Location itself.
3863 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3866 lm->set_position (copy_location->start(), copy_location->end());
3871 assert (!_copied_locations.empty());
3873 show_verbose_cursor_time (newframe);
3877 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3879 if (!movement_occurred) {
3881 if (was_double_click()) {
3882 _editor->rename_marker (_marker);
3886 /* just a click, do nothing but finish
3887 off the selection process
3890 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3893 case Selection::Set:
3894 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3895 _editor->selection->set (_marker);
3899 case Selection::Toggle:
3900 /* we toggle on the button release, click only */
3901 _editor->selection->toggle (_marker);
3904 case Selection::Extend:
3905 case Selection::Add:
3912 _editor->_dragging_edit_point = false;
3914 _editor->begin_reversible_command ( _("move marker") );
3915 XMLNode &before = _editor->session()->locations()->get_state();
3917 MarkerSelection::iterator i;
3918 CopiedLocationInfo::iterator x;
3921 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3922 x != _copied_locations.end() && i != _editor->selection->markers.end();
3925 Location * location = _editor->find_location_from_marker (*i, is_start);
3929 if (location->locked()) {
3933 if (location->is_mark()) {
3934 location->set_start (((*x).location)->start());
3936 location->set (((*x).location)->start(), ((*x).location)->end());
3941 XMLNode &after = _editor->session()->locations()->get_state();
3942 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3943 _editor->commit_reversible_command ();
3947 MarkerDrag::aborted (bool movement_occured)
3949 if (!movement_occured) {
3953 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3955 /* move all markers to their original location */
3958 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3961 Location * location = _editor->find_location_from_marker (*m, is_start);
3964 (*m)->set_position (is_start ? location->start() : location->end());
3971 MarkerDrag::update_item (Location*)
3976 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3978 _cumulative_x_drag (0),
3979 _cumulative_y_drag (0)
3981 if (_zero_gain_fraction < 0.0) {
3982 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3985 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3987 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3993 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3995 Drag::start_grab (event, _editor->cursors()->fader);
3997 // start the grab at the center of the control point so
3998 // the point doesn't 'jump' to the mouse after the first drag
3999 _fixed_grab_x = _point->get_x();
4000 _fixed_grab_y = _point->get_y();
4002 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4003 setup_snap_delta (pos);
4005 float const fraction = 1 - (_point->get_y() / _point->line().height());
4007 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
4009 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4011 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
4013 if (!_point->can_slide ()) {
4014 _x_constrained = true;
4019 ControlPointDrag::motion (GdkEvent* event, bool)
4021 double dx = _drags->current_pointer_x() - last_pointer_x();
4022 double dy = current_pointer_y() - last_pointer_y();
4024 if (event->button.state & Keyboard::SecondaryModifier) {
4029 /* coordinate in pixels relative to the start of the region (for region-based automation)
4030 or track (for track-based automation) */
4031 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4032 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4034 // calculate zero crossing point. back off by .01 to stay on the
4035 // positive side of zero
4036 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4038 // make sure we hit zero when passing through
4039 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4043 if (_x_constrained) {
4046 if (_y_constrained) {
4050 _cumulative_x_drag = cx - _fixed_grab_x;
4051 _cumulative_y_drag = cy - _fixed_grab_y;
4055 cy = min ((double) _point->line().height(), cy);
4057 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta ();
4059 if (!_x_constrained) {
4060 _editor->snap_to_with_modifier (cx_frames, event);
4063 cx_frames -= snap_delta ();
4064 cx_frames = min (cx_frames, _point->line().maximum_time());
4066 float const fraction = 1.0 - (cy / _point->line().height());
4068 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4070 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4074 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4076 if (!movement_occurred) {
4080 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4081 _editor->reset_point_selection ();
4085 motion (event, false);
4088 _point->line().end_drag (_pushing, _final_index);
4089 _editor->commit_reversible_command ();
4093 ControlPointDrag::aborted (bool)
4095 _point->line().reset ();
4099 ControlPointDrag::active (Editing::MouseMode m)
4101 if (m == Editing::MouseDraw) {
4102 /* always active in mouse draw */
4106 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4107 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4110 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4113 , _cumulative_y_drag (0)
4115 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4119 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4121 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4124 _item = &_line->grab_item ();
4126 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4127 origin, and ditto for y.
4130 double cx = event->button.x;
4131 double cy = event->button.y;
4133 _line->parent_group().canvas_to_item (cx, cy);
4135 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4140 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
4141 /* no adjacent points */
4145 Drag::start_grab (event, _editor->cursors()->fader);
4147 /* store grab start in parent frame */
4152 double fraction = 1.0 - (cy / _line->height());
4154 _line->start_drag_line (before, after, fraction);
4156 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4160 LineDrag::motion (GdkEvent* event, bool)
4162 double dy = current_pointer_y() - last_pointer_y();
4164 if (event->button.state & Keyboard::SecondaryModifier) {
4168 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4170 _cumulative_y_drag = cy - _fixed_grab_y;
4173 cy = min ((double) _line->height(), cy);
4175 double const fraction = 1.0 - (cy / _line->height());
4178 /* we are ignoring x position for this drag, so we can just pass in anything */
4179 _line->drag_motion (0, fraction, true, false, ignored);
4181 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4185 LineDrag::finished (GdkEvent* event, bool movement_occured)
4187 if (movement_occured) {
4188 motion (event, false);
4189 _line->end_drag (false, 0);
4191 /* add a new control point on the line */
4193 AutomationTimeAxisView* atv;
4195 _line->end_drag (false, 0);
4197 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4198 framepos_t where = _editor->window_event_sample (event, 0, 0);
4199 atv->add_automation_event (event, where, event->button.y, false);
4203 _editor->commit_reversible_command ();
4207 LineDrag::aborted (bool)
4212 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4215 _cumulative_x_drag (0)
4217 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4221 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4223 Drag::start_grab (event);
4225 _line = reinterpret_cast<Line*> (_item);
4228 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4230 double cx = event->button.x;
4231 double cy = event->button.y;
4233 _item->parent()->canvas_to_item (cx, cy);
4235 /* store grab start in parent frame */
4236 _region_view_grab_x = cx;
4238 _before = *(float*) _item->get_data ("position");
4240 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4242 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4246 FeatureLineDrag::motion (GdkEvent*, bool)
4248 double dx = _drags->current_pointer_x() - last_pointer_x();
4250 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4252 _cumulative_x_drag += dx;
4254 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4263 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4265 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4267 float *pos = new float;
4270 _line->set_data ("position", pos);
4276 FeatureLineDrag::finished (GdkEvent*, bool)
4278 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4279 _arv->update_transient(_before, _before);
4283 FeatureLineDrag::aborted (bool)
4288 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4290 , _vertical_only (false)
4292 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4296 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4298 Drag::start_grab (event);
4299 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4303 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4310 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4312 framepos_t grab = grab_frame ();
4313 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4314 _editor->snap_to_with_modifier (grab, event);
4316 grab = raw_grab_frame ();
4319 /* base start and end on initial click position */
4329 if (current_pointer_y() < grab_y()) {
4330 y1 = current_pointer_y();
4333 y2 = current_pointer_y();
4337 if (start != end || y1 != y2) {
4339 double x1 = _editor->sample_to_pixel (start);
4340 double x2 = _editor->sample_to_pixel (end);
4341 const double min_dimension = 2.0;
4343 if (_vertical_only) {
4344 /* fixed 10 pixel width */
4348 x2 = min (x1 - min_dimension, x2);
4350 x2 = max (x1 + min_dimension, x2);
4355 y2 = min (y1 - min_dimension, y2);
4357 y2 = max (y1 + min_dimension, y2);
4360 /* translate rect into item space and set */
4362 ArdourCanvas::Rect r (x1, y1, x2, y2);
4364 /* this drag is a _trackview_only == true drag, so the y1 and
4365 * y2 (computed using current_pointer_y() and grab_y()) will be
4366 * relative to the top of the trackview group). The
4367 * rubberband rect has the same parent/scroll offset as the
4368 * the trackview group, so we can use the "r" rect directly
4369 * to set the shape of the rubberband.
4372 _editor->rubberband_rect->set (r);
4373 _editor->rubberband_rect->show();
4374 _editor->rubberband_rect->raise_to_top();
4376 show_verbose_cursor_time (pf);
4378 do_select_things (event, true);
4383 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4387 framepos_t grab = grab_frame ();
4388 framepos_t lpf = last_pointer_frame ();
4390 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4391 grab = raw_grab_frame ();
4392 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4406 if (current_pointer_y() < grab_y()) {
4407 y1 = current_pointer_y();
4410 y2 = current_pointer_y();
4414 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4418 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4420 if (movement_occurred) {
4422 motion (event, false);
4423 do_select_things (event, false);
4429 bool do_deselect = true;
4430 MidiTimeAxisView* mtv;
4432 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4434 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4435 /* nothing selected */
4436 add_midi_region (mtv);
4437 do_deselect = false;
4441 /* do not deselect if Primary or Tertiary (toggle-select or
4442 * extend-select are pressed.
4445 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4446 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4453 _editor->rubberband_rect->hide();
4457 RubberbandSelectDrag::aborted (bool)
4459 _editor->rubberband_rect->hide ();
4462 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4463 : RegionDrag (e, i, p, v)
4465 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4469 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4471 Drag::start_grab (event, cursor);
4473 _editor->get_selection().add (_primary);
4475 framepos_t where = _primary->region()->position();
4476 setup_snap_delta (where);
4478 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4482 TimeFXDrag::motion (GdkEvent* event, bool)
4484 RegionView* rv = _primary;
4485 StreamView* cv = rv->get_time_axis_view().view ();
4487 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4488 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4489 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4490 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta ();
4491 _editor->snap_to_with_modifier (pf, event);
4492 pf -= snap_delta ();
4494 if (pf > rv->region()->position()) {
4495 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4498 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4502 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4504 _primary->get_time_axis_view().hide_timestretch ();
4506 if (!movement_occurred) {
4510 if (last_pointer_frame() < _primary->region()->position()) {
4511 /* backwards drag of the left edge - not usable */
4515 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4517 float percentage = (double) newlen / (double) _primary->region()->length();
4519 #ifndef USE_RUBBERBAND
4520 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4521 if (_primary->region()->data_type() == DataType::AUDIO) {
4522 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4526 if (!_editor->get_selection().regions.empty()) {
4527 /* primary will already be included in the selection, and edit
4528 group shared editing will propagate selection across
4529 equivalent regions, so just use the current region
4533 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4534 error << _("An error occurred while executing time stretch operation") << endmsg;
4540 TimeFXDrag::aborted (bool)
4542 _primary->get_time_axis_view().hide_timestretch ();
4545 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4548 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4552 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4554 Drag::start_grab (event);
4558 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4560 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4564 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4566 if (movement_occurred && _editor->session()) {
4567 /* make sure we stop */
4568 _editor->session()->request_transport_speed (0.0);
4573 ScrubDrag::aborted (bool)
4578 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4582 , _time_selection_at_start (!_editor->get_selection().time.empty())
4584 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4586 if (_time_selection_at_start) {
4587 start_at_start = _editor->get_selection().time.start();
4588 end_at_start = _editor->get_selection().time.end_frame();
4593 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4595 if (_editor->session() == 0) {
4599 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4601 switch (_operation) {
4602 case CreateSelection:
4603 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4608 cursor = _editor->cursors()->selector;
4609 Drag::start_grab (event, cursor);
4612 case SelectionStartTrim:
4613 if (_editor->clicked_axisview) {
4614 _editor->clicked_axisview->order_selection_trims (_item, true);
4616 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4619 case SelectionEndTrim:
4620 if (_editor->clicked_axisview) {
4621 _editor->clicked_axisview->order_selection_trims (_item, false);
4623 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4627 Drag::start_grab (event, cursor);
4630 case SelectionExtend:
4631 Drag::start_grab (event, cursor);
4635 if (_operation == SelectionMove) {
4636 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4638 show_verbose_cursor_time (adjusted_current_frame (event));
4643 SelectionDrag::setup_pointer_frame_offset ()
4645 switch (_operation) {
4646 case CreateSelection:
4647 _pointer_frame_offset = 0;
4650 case SelectionStartTrim:
4652 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4655 case SelectionEndTrim:
4656 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4659 case SelectionExtend:
4665 SelectionDrag::motion (GdkEvent* event, bool first_move)
4667 framepos_t start = 0;
4669 framecnt_t length = 0;
4670 framecnt_t distance = 0;
4672 framepos_t const pending_position = adjusted_current_frame (event);
4674 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4678 switch (_operation) {
4679 case CreateSelection:
4681 framepos_t grab = grab_frame ();
4684 grab = adjusted_current_frame (event, false);
4685 if (grab < pending_position) {
4686 _editor->snap_to (grab, RoundDownMaybe);
4688 _editor->snap_to (grab, RoundUpMaybe);
4692 if (pending_position < grab) {
4693 start = pending_position;
4696 end = pending_position;
4700 /* first drag: Either add to the selection
4701 or create a new selection
4708 /* adding to the selection */
4709 _editor->set_selected_track_as_side_effect (Selection::Add);
4710 _editor->clicked_selection = _editor->selection->add (start, end);
4717 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4718 _editor->set_selected_track_as_side_effect (Selection::Set);
4721 _editor->clicked_selection = _editor->selection->set (start, end);
4725 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4726 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4727 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4729 _editor->selection->add (atest);
4733 /* select all tracks within the rectangle that we've marked out so far */
4734 TrackViewList new_selection;
4735 TrackViewList& all_tracks (_editor->track_views);
4737 ArdourCanvas::Coord const top = grab_y();
4738 ArdourCanvas::Coord const bottom = current_pointer_y();
4740 if (top >= 0 && bottom >= 0) {
4742 //first, find the tracks that are covered in the y range selection
4743 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4744 if ((*i)->covered_by_y_range (top, bottom)) {
4745 new_selection.push_back (*i);
4749 //now find any tracks that are GROUPED with the tracks we selected
4750 TrackViewList grouped_add = new_selection;
4751 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4752 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4753 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4754 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4755 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4756 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4757 grouped_add.push_back (*j);
4762 //now compare our list with the current selection, and add or remove as necessary
4763 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4764 TrackViewList tracks_to_add;
4765 TrackViewList tracks_to_remove;
4766 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4767 if ( !_editor->selection->tracks.contains ( *i ) )
4768 tracks_to_add.push_back ( *i );
4769 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4770 if ( !grouped_add.contains ( *i ) )
4771 tracks_to_remove.push_back ( *i );
4772 _editor->selection->add(tracks_to_add);
4773 _editor->selection->remove(tracks_to_remove);
4779 case SelectionStartTrim:
4781 start = _editor->selection->time[_editor->clicked_selection].start;
4782 end = _editor->selection->time[_editor->clicked_selection].end;
4784 if (pending_position > end) {
4787 start = pending_position;
4791 case SelectionEndTrim:
4793 start = _editor->selection->time[_editor->clicked_selection].start;
4794 end = _editor->selection->time[_editor->clicked_selection].end;
4796 if (pending_position < start) {
4799 end = pending_position;
4806 start = _editor->selection->time[_editor->clicked_selection].start;
4807 end = _editor->selection->time[_editor->clicked_selection].end;
4809 length = end - start;
4810 distance = pending_position - start;
4811 start = pending_position;
4812 _editor->snap_to (start);
4814 end = start + length;
4818 case SelectionExtend:
4823 switch (_operation) {
4825 if (_time_selection_at_start) {
4826 _editor->selection->move_time (distance);
4830 _editor->selection->replace (_editor->clicked_selection, start, end);
4834 if (_operation == SelectionMove) {
4835 show_verbose_cursor_time(start);
4837 show_verbose_cursor_time(pending_position);
4842 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4844 Session* s = _editor->session();
4846 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4847 if (movement_occurred) {
4848 motion (event, false);
4849 /* XXX this is not object-oriented programming at all. ick */
4850 if (_editor->selection->time.consolidate()) {
4851 _editor->selection->TimeChanged ();
4854 /* XXX what if its a music time selection? */
4856 if ( s->get_play_range() && s->transport_rolling() ) {
4857 s->request_play_range (&_editor->selection->time, true);
4859 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4860 if (_operation == SelectionEndTrim)
4861 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4863 s->request_locate (_editor->get_selection().time.start());
4869 /* just a click, no pointer movement.
4872 if (_operation == SelectionExtend) {
4873 if (_time_selection_at_start) {
4874 framepos_t pos = adjusted_current_frame (event, false);
4875 framepos_t start = min (pos, start_at_start);
4876 framepos_t end = max (pos, end_at_start);
4877 _editor->selection->set (start, end);
4880 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4881 if (_editor->clicked_selection) {
4882 _editor->selection->remove (_editor->clicked_selection);
4885 if (!_editor->clicked_selection) {
4886 _editor->selection->clear_time();
4891 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4892 _editor->selection->set (_editor->clicked_axisview);
4895 if (s && s->get_play_range () && s->transport_rolling()) {
4896 s->request_stop (false, false);
4901 _editor->stop_canvas_autoscroll ();
4902 _editor->clicked_selection = 0;
4903 _editor->commit_reversible_selection_op ();
4907 SelectionDrag::aborted (bool)
4912 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4913 : Drag (e, i, false),
4917 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4919 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4920 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4921 physical_screen_height (_editor->get_window())));
4922 _drag_rect->hide ();
4924 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4925 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4928 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4930 /* normal canvas items will be cleaned up when their parent group is deleted. But
4931 this item is created as the child of a long-lived parent group, and so we
4932 need to explicitly delete it.
4938 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4940 if (_editor->session() == 0) {
4944 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4946 if (!_editor->temp_location) {
4947 _editor->temp_location = new Location (*_editor->session());
4950 switch (_operation) {
4951 case CreateSkipMarker:
4952 case CreateRangeMarker:
4953 case CreateTransportMarker:
4954 case CreateCDMarker:
4956 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4961 cursor = _editor->cursors()->selector;
4965 Drag::start_grab (event, cursor);
4967 show_verbose_cursor_time (adjusted_current_frame (event));
4971 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4973 framepos_t start = 0;
4975 ArdourCanvas::Rectangle *crect;
4977 switch (_operation) {
4978 case CreateSkipMarker:
4979 crect = _editor->range_bar_drag_rect;
4981 case CreateRangeMarker:
4982 crect = _editor->range_bar_drag_rect;
4984 case CreateTransportMarker:
4985 crect = _editor->transport_bar_drag_rect;
4987 case CreateCDMarker:
4988 crect = _editor->cd_marker_bar_drag_rect;
4991 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4996 framepos_t const pf = adjusted_current_frame (event);
4998 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4999 framepos_t grab = grab_frame ();
5000 _editor->snap_to (grab);
5002 if (pf < grab_frame()) {
5010 /* first drag: Either add to the selection
5011 or create a new selection.
5016 _editor->temp_location->set (start, end);
5020 update_item (_editor->temp_location);
5022 //_drag_rect->raise_to_top();
5028 _editor->temp_location->set (start, end);
5030 double x1 = _editor->sample_to_pixel (start);
5031 double x2 = _editor->sample_to_pixel (end);
5035 update_item (_editor->temp_location);
5038 show_verbose_cursor_time (pf);
5043 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5045 Location * newloc = 0;
5049 if (movement_occurred) {
5050 motion (event, false);
5053 switch (_operation) {
5054 case CreateSkipMarker:
5055 case CreateRangeMarker:
5056 case CreateCDMarker:
5058 XMLNode &before = _editor->session()->locations()->get_state();
5059 if (_operation == CreateSkipMarker) {
5060 _editor->begin_reversible_command (_("new skip marker"));
5061 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5062 flags = Location::IsRangeMarker | Location::IsSkip;
5063 _editor->range_bar_drag_rect->hide();
5064 } else if (_operation == CreateCDMarker) {
5065 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5066 _editor->begin_reversible_command (_("new CD marker"));
5067 flags = Location::IsRangeMarker | Location::IsCDMarker;
5068 _editor->cd_marker_bar_drag_rect->hide();
5070 _editor->begin_reversible_command (_("new skip marker"));
5071 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5072 flags = Location::IsRangeMarker;
5073 _editor->range_bar_drag_rect->hide();
5075 newloc = new Location (
5076 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5079 _editor->session()->locations()->add (newloc, true);
5080 XMLNode &after = _editor->session()->locations()->get_state();
5081 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5082 _editor->commit_reversible_command ();
5086 case CreateTransportMarker:
5087 // popup menu to pick loop or punch
5088 _editor->new_transport_marker_context_menu (&event->button, _item);
5094 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5096 if (_operation == CreateTransportMarker) {
5098 /* didn't drag, so just locate */
5100 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5102 } else if (_operation == CreateCDMarker) {
5104 /* didn't drag, but mark is already created so do
5107 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5112 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5114 if (end == max_framepos) {
5115 end = _editor->session()->current_end_frame ();
5118 if (start == max_framepos) {
5119 start = _editor->session()->current_start_frame ();
5122 switch (_editor->mouse_mode) {
5124 /* find the two markers on either side and then make the selection from it */
5125 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5129 /* find the two markers on either side of the click and make the range out of it */
5130 _editor->selection->set (start, end);
5139 _editor->stop_canvas_autoscroll ();
5143 RangeMarkerBarDrag::aborted (bool movement_occured)
5145 if (movement_occured) {
5146 _drag_rect->hide ();
5151 RangeMarkerBarDrag::update_item (Location* location)
5153 double const x1 = _editor->sample_to_pixel (location->start());
5154 double const x2 = _editor->sample_to_pixel (location->end());
5156 _drag_rect->set_x0 (x1);
5157 _drag_rect->set_x1 (x2);
5160 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5162 , _cumulative_dx (0)
5163 , _cumulative_dy (0)
5165 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5167 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5169 _region = &_primary->region_view ();
5170 _note_height = _region->midi_stream_view()->note_height ();
5174 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5176 Drag::start_grab (event);
5177 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5179 if (!(_was_selected = _primary->selected())) {
5181 /* tertiary-click means extend selection - we'll do that on button release,
5182 so don't add it here, because otherwise we make it hard to figure
5183 out the "extend-to" range.
5186 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5189 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5192 _region->note_selected (_primary, true);
5194 _region->unique_select (_primary);
5197 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5198 _editor->commit_reversible_selection_op();
5203 /** @return Current total drag x change in frames */
5205 NoteDrag::total_dx () const
5208 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5210 /* primary note time */
5211 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5213 /* new time of the primary note in session frames */
5214 frameoffset_t st = n + dx + snap_delta ();
5216 framepos_t const rp = _region->region()->position ();
5218 /* prevent the note being dragged earlier than the region's position */
5221 /* snap and return corresponding delta */
5222 return _region->snap_frame_to_frame (st - rp) + rp - n - snap_delta ();
5225 /** @return Current total drag y change in note number */
5227 NoteDrag::total_dy () const
5229 MidiStreamView* msv = _region->midi_stream_view ();
5230 double const y = _region->midi_view()->y_position ();
5231 /* new current note */
5232 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5234 n = max (msv->lowest_note(), n);
5235 n = min (msv->highest_note(), n);
5236 /* and work out delta */
5237 return n - msv->y_to_note (grab_y() - y);
5241 NoteDrag::motion (GdkEvent *, bool)
5243 /* Total change in x and y since the start of the drag */
5244 frameoffset_t const dx = total_dx ();
5245 int8_t const dy = total_dy ();
5247 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5248 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5249 double const tdy = -dy * _note_height - _cumulative_dy;
5252 _cumulative_dx += tdx;
5253 _cumulative_dy += tdy;
5255 int8_t note_delta = total_dy();
5257 _region->move_selection (tdx, tdy, note_delta);
5259 /* the new note value may be the same as the old one, but we
5260 * don't know what that means because the selection may have
5261 * involved more than one note and we might be doing something
5262 * odd with them. so show the note value anyway, always.
5266 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5268 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5269 (int) floor ((double)new_note));
5271 show_verbose_cursor_text (buf);
5276 NoteDrag::finished (GdkEvent* ev, bool moved)
5279 /* no motion - select note */
5281 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5282 _editor->current_mouse_mode() == Editing::MouseDraw) {
5284 bool changed = false;
5286 if (_was_selected) {
5287 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5289 _region->note_deselected (_primary);
5293 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5294 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5296 if (!extend && !add && _region->selection_size() > 1) {
5297 _region->unique_select (_primary);
5299 } else if (extend) {
5300 _region->note_selected (_primary, true, true);
5303 /* it was added during button press */
5308 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5309 _editor->commit_reversible_selection_op();
5313 _region->note_dropped (_primary, total_dx(), total_dy());
5318 NoteDrag::aborted (bool)
5323 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5324 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5325 : Drag (editor, atv->base_item ())
5327 , _y_origin (atv->y_position())
5328 , _nothing_to_drag (false)
5330 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5331 setup (atv->lines ());
5334 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5335 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5336 : Drag (editor, rv->get_canvas_group ())
5338 , _y_origin (rv->get_time_axis_view().y_position())
5339 , _nothing_to_drag (false)
5342 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5344 list<boost::shared_ptr<AutomationLine> > lines;
5346 AudioRegionView* audio_view;
5347 AutomationRegionView* automation_view;
5348 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5349 lines.push_back (audio_view->get_gain_line ());
5350 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5351 lines.push_back (automation_view->line ());
5354 error << _("Automation range drag created for invalid region type") << endmsg;
5360 /** @param lines AutomationLines to drag.
5361 * @param offset Offset from the session start to the points in the AutomationLines.
5364 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5366 /* find the lines that overlap the ranges being dragged */
5367 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5368 while (i != lines.end ()) {
5369 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5372 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5374 /* check this range against all the AudioRanges that we are using */
5375 list<AudioRange>::const_iterator k = _ranges.begin ();
5376 while (k != _ranges.end()) {
5377 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5383 /* add it to our list if it overlaps at all */
5384 if (k != _ranges.end()) {
5389 _lines.push_back (n);
5395 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5399 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5401 return 1.0 - ((global_y - _y_origin) / line->height());
5405 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5407 const double v = list->eval(x);
5408 return _integral ? rint(v) : v;
5412 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5414 Drag::start_grab (event, cursor);
5416 /* Get line states before we start changing things */
5417 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5418 i->state = &i->line->get_state ();
5419 i->original_fraction = y_fraction (i->line, current_pointer_y());
5422 if (_ranges.empty()) {
5424 /* No selected time ranges: drag all points */
5425 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5426 uint32_t const N = i->line->npoints ();
5427 for (uint32_t j = 0; j < N; ++j) {
5428 i->points.push_back (i->line->nth (j));
5434 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5436 framecnt_t const half = (i->start + i->end) / 2;
5438 /* find the line that this audio range starts in */
5439 list<Line>::iterator j = _lines.begin();
5440 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5444 if (j != _lines.end()) {
5445 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5447 /* j is the line that this audio range starts in; fade into it;
5448 64 samples length plucked out of thin air.
5451 framepos_t a = i->start + 64;
5456 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5457 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5459 the_list->editor_add (p, value (the_list, p));
5460 the_list->editor_add (q, value (the_list, q));
5463 /* same thing for the end */
5466 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5470 if (j != _lines.end()) {
5471 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5473 /* j is the line that this audio range starts in; fade out of it;
5474 64 samples length plucked out of thin air.
5477 framepos_t b = i->end - 64;
5482 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5483 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5485 the_list->editor_add (p, value (the_list, p));
5486 the_list->editor_add (q, value (the_list, q));
5490 _nothing_to_drag = true;
5492 /* Find all the points that should be dragged and put them in the relevant
5493 points lists in the Line structs.
5496 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5498 uint32_t const N = i->line->npoints ();
5499 for (uint32_t j = 0; j < N; ++j) {
5501 /* here's a control point on this line */
5502 ControlPoint* p = i->line->nth (j);
5503 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5505 /* see if it's inside a range */
5506 list<AudioRange>::const_iterator k = _ranges.begin ();
5507 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5511 if (k != _ranges.end()) {
5512 /* dragging this point */
5513 _nothing_to_drag = false;
5514 i->points.push_back (p);
5520 if (_nothing_to_drag) {
5524 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5525 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5530 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5532 if (_nothing_to_drag) {
5536 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5537 float const f = y_fraction (l->line, current_pointer_y());
5538 /* we are ignoring x position for this drag, so we can just pass in anything */
5540 l->line->drag_motion (0, f, true, false, ignored);
5541 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5546 AutomationRangeDrag::finished (GdkEvent* event, bool)
5548 if (_nothing_to_drag) {
5552 motion (event, false);
5553 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5554 i->line->end_drag (false, 0);
5557 _editor->commit_reversible_command ();
5561 AutomationRangeDrag::aborted (bool)
5563 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5568 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5570 , initial_time_axis_view (itav)
5572 /* note that time_axis_view may be null if the regionview was created
5573 * as part of a copy operation.
5575 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5576 layer = v->region()->layer ();
5577 initial_y = v->get_canvas_group()->position().y;
5578 initial_playlist = v->region()->playlist ();
5579 initial_position = v->region()->position ();
5580 initial_end = v->region()->position () + v->region()->length ();
5583 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5584 : Drag (e, i->canvas_item ())
5587 , _cumulative_dx (0)
5589 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5590 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5595 PatchChangeDrag::motion (GdkEvent* ev, bool)
5597 framepos_t f = adjusted_current_frame (ev);
5598 boost::shared_ptr<Region> r = _region_view->region ();
5599 f = max (f, r->position ());
5600 f = min (f, r->last_frame ());
5602 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5603 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5604 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5605 _cumulative_dx = dxu;
5609 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5611 if (!movement_occurred) {
5615 boost::shared_ptr<Region> r (_region_view->region ());
5616 framepos_t f = adjusted_current_frame (ev);
5617 f = max (f, r->position ());
5618 f = min (f, r->last_frame ());
5620 _region_view->move_patch_change (
5622 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5627 PatchChangeDrag::aborted (bool)
5629 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5633 PatchChangeDrag::setup_pointer_frame_offset ()
5635 boost::shared_ptr<Region> region = _region_view->region ();
5636 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5639 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5640 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5647 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5649 _region_view->update_drag_selection (
5651 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5655 MidiRubberbandSelectDrag::deselect_things ()
5660 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5661 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5664 _vertical_only = true;
5668 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5670 double const y = _region_view->midi_view()->y_position ();
5672 y1 = max (0.0, y1 - y);
5673 y2 = max (0.0, y2 - y);
5675 _region_view->update_vertical_drag_selection (
5678 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5683 MidiVerticalSelectDrag::deselect_things ()
5688 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5689 : RubberbandSelectDrag (e, i)
5695 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5697 if (drag_in_progress) {
5698 /* We just want to select things at the end of the drag, not during it */
5702 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5704 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5706 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5708 _editor->commit_reversible_selection_op ();
5712 EditorRubberbandSelectDrag::deselect_things ()
5714 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5716 _editor->selection->clear_tracks();
5717 _editor->selection->clear_regions();
5718 _editor->selection->clear_points ();
5719 _editor->selection->clear_lines ();
5720 _editor->selection->clear_midi_notes ();
5722 _editor->commit_reversible_selection_op();
5725 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5730 _note[0] = _note[1] = 0;
5733 NoteCreateDrag::~NoteCreateDrag ()
5739 NoteCreateDrag::grid_frames (framepos_t t) const
5742 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5744 grid_beats = Evoral::Beats(1);
5747 return _region_view->region_beats_to_region_frames (grid_beats);
5751 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5753 Drag::start_grab (event, cursor);
5755 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5757 framepos_t pf = _drags->current_pointer_frame ();
5758 framecnt_t const g = grid_frames (pf);
5760 /* Hack so that we always snap to the note that we are over, instead of snapping
5761 to the next one if we're more than halfway through the one we're over.
5763 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5767 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5768 _note[1] = _note[0];
5770 MidiStreamView* sv = _region_view->midi_stream_view ();
5771 double const x = _editor->sample_to_pixel (_note[0]);
5772 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5774 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5775 _drag_rect->set_outline_all ();
5776 _drag_rect->set_outline_color (0xffffff99);
5777 _drag_rect->set_fill_color (0xffffff66);
5781 NoteCreateDrag::motion (GdkEvent* event, bool)
5783 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5784 double const x0 = _editor->sample_to_pixel (_note[0]);
5785 double const x1 = _editor->sample_to_pixel (_note[1]);
5786 _drag_rect->set_x0 (std::min(x0, x1));
5787 _drag_rect->set_x1 (std::max(x0, x1));
5791 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5793 if (!had_movement) {
5797 framepos_t const start = min (_note[0], _note[1]);
5798 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5800 framecnt_t const g = grid_frames (start);
5801 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5803 if (_editor->snap_mode() == SnapNormal && length < g) {
5807 Evoral::Beats length_beats = max (
5808 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5810 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5814 NoteCreateDrag::y_to_region (double y) const
5817 _region_view->get_canvas_group()->canvas_to_item (x, y);
5822 NoteCreateDrag::aborted (bool)
5827 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5832 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5836 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5838 Drag::start_grab (event, cursor);
5842 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5848 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5851 distance = _drags->current_pointer_x() - grab_x();
5852 len = ar->fade_in()->back()->when;
5854 distance = grab_x() - _drags->current_pointer_x();
5855 len = ar->fade_out()->back()->when;
5858 /* how long should it be ? */
5860 new_length = len + _editor->pixel_to_sample (distance);
5862 /* now check with the region that this is legal */
5864 new_length = ar->verify_xfade_bounds (new_length, start);
5867 arv->reset_fade_in_shape_width (ar, new_length);
5869 arv->reset_fade_out_shape_width (ar, new_length);
5874 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5880 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5883 distance = _drags->current_pointer_x() - grab_x();
5884 len = ar->fade_in()->back()->when;
5886 distance = grab_x() - _drags->current_pointer_x();
5887 len = ar->fade_out()->back()->when;
5890 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5892 _editor->begin_reversible_command ("xfade trim");
5893 ar->playlist()->clear_owned_changes ();
5896 ar->set_fade_in_length (new_length);
5898 ar->set_fade_out_length (new_length);
5901 /* Adjusting the xfade may affect other regions in the playlist, so we need
5902 to get undo Commands from the whole playlist rather than just the
5906 vector<Command*> cmds;
5907 ar->playlist()->rdiff (cmds);
5908 _editor->session()->add_commands (cmds);
5909 _editor->commit_reversible_command ();
5914 CrossfadeEdgeDrag::aborted (bool)
5917 // arv->redraw_start_xfade ();
5919 // arv->redraw_end_xfade ();
5923 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5924 : Drag (e, item, true)
5925 , line (new EditorCursor (*e))
5927 line->set_position (pos);
5931 RegionCutDrag::~RegionCutDrag ()
5937 RegionCutDrag::motion (GdkEvent*, bool)
5939 framepos_t where = _drags->current_pointer_frame();
5940 _editor->snap_to (where);
5942 line->set_position (where);
5946 RegionCutDrag::finished (GdkEvent*, bool)
5948 _editor->get_track_canvas()->canvas()->re_enter();
5950 framepos_t pos = _drags->current_pointer_frame();
5954 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5960 _editor->split_regions_at (pos, rs);
5964 RegionCutDrag::aborted (bool)