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)
234 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
240 _cursor_ctx = CursorContext::create (*_editor, cursor);
242 _cursor_ctx->change (cursor);
249 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
251 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
253 if (Keyboard::is_button2_event (&event->button)) {
254 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
255 _y_constrained = true;
256 _x_constrained = false;
258 _y_constrained = false;
259 _x_constrained = true;
262 _x_constrained = false;
263 _y_constrained = false;
266 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
267 setup_pointer_frame_offset ();
268 _grab_frame = adjusted_frame (_raw_grab_frame, event);
269 _last_pointer_frame = _grab_frame;
270 _last_pointer_x = _grab_x;
272 if (_trackview_only) {
273 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
276 _last_pointer_y = _grab_y;
280 if (!_editor->cursors()->is_invalid (cursor)) {
281 /* CAIROCANVAS need a variant here that passes *cursor */
282 _cursor_ctx = CursorContext::create (*_editor, cursor);
285 if (_editor->session() && _editor->session()->transport_rolling()) {
288 _was_rolling = false;
291 switch (_editor->snap_type()) {
292 case SnapToRegionStart:
293 case SnapToRegionEnd:
294 case SnapToRegionSync:
295 case SnapToRegionBoundary:
296 _editor->build_region_boundary_cache ();
303 /** Call to end a drag `successfully'. Ungrabs item and calls
304 * subclass' finished() method.
306 * @param event GDK event, or 0.
307 * @return true if some movement occurred, otherwise false.
310 Drag::end_grab (GdkEvent* event)
312 _editor->stop_canvas_autoscroll ();
316 finished (event, _move_threshold_passed);
318 _editor->verbose_cursor()->hide ();
321 return _move_threshold_passed;
325 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
329 if (f > _pointer_frame_offset) {
330 pos = f - _pointer_frame_offset;
334 _editor->snap_to_with_modifier (pos, event);
341 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
343 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
347 Drag::current_pointer_x() const
349 return _drags->current_pointer_x ();
353 Drag::current_pointer_y () const
355 if (!_trackview_only) {
356 return _drags->current_pointer_y ();
359 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
363 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
365 /* check to see if we have moved in any way that matters since the last motion event */
366 if (_move_threshold_passed &&
367 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
368 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
372 pair<framecnt_t, int> const threshold = move_threshold ();
374 bool const old_move_threshold_passed = _move_threshold_passed;
376 if (!_move_threshold_passed) {
378 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
379 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
381 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
384 if (active (_editor->mouse_mode) && _move_threshold_passed) {
386 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
388 if (old_move_threshold_passed != _move_threshold_passed) {
392 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
393 _initially_vertical = true;
395 _initially_vertical = false;
399 if (!from_autoscroll) {
400 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
403 if (!_editor->autoscroll_active() || from_autoscroll) {
406 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
408 motion (event, first_move && !_starting_point_passed);
410 if (first_move && !_starting_point_passed) {
411 _starting_point_passed = true;
414 _last_pointer_x = _drags->current_pointer_x ();
415 _last_pointer_y = current_pointer_y ();
416 _last_pointer_frame = adjusted_current_frame (event);
426 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
434 aborted (_move_threshold_passed);
436 _editor->stop_canvas_autoscroll ();
437 _editor->verbose_cursor()->hide ();
441 Drag::show_verbose_cursor_time (framepos_t frame)
443 _editor->verbose_cursor()->set_time (frame);
444 _editor->verbose_cursor()->show ();
448 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
450 _editor->verbose_cursor()->set_duration (start, end);
451 _editor->verbose_cursor()->show ();
455 Drag::show_verbose_cursor_text (string const & text)
457 _editor->verbose_cursor()->set (text);
458 _editor->verbose_cursor()->show ();
461 boost::shared_ptr<Region>
462 Drag::add_midi_region (MidiTimeAxisView* view)
464 if (_editor->session()) {
465 const TempoMap& map (_editor->session()->tempo_map());
466 framecnt_t pos = grab_frame();
467 const Meter& m = map.meter_at (pos);
468 /* not that the frame rate used here can be affected by pull up/down which
471 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
472 return view->add_region (grab_frame(), len, true);
475 return boost::shared_ptr<Region>();
478 struct EditorOrderTimeAxisViewSorter {
479 bool operator() (TimeAxisView* a, TimeAxisView* b) {
480 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
481 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
483 return ra->route()->order_key () < rb->route()->order_key ();
487 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
492 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
494 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
495 as some of the regions we are dragging may be on such tracks.
498 TrackViewList track_views = _editor->track_views;
499 track_views.sort (EditorOrderTimeAxisViewSorter ());
501 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
502 _time_axis_views.push_back (*i);
504 TimeAxisView::Children children_list = (*i)->get_child_list ();
505 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
506 _time_axis_views.push_back (j->get());
510 /* the list of views can be empty at this point if this is a region list-insert drag
513 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
514 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
517 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
521 RegionDrag::region_going_away (RegionView* v)
523 list<DraggingView>::iterator i = _views.begin ();
524 while (i != _views.end() && i->view != v) {
528 if (i != _views.end()) {
533 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
534 * or -1 if it is not found.
537 RegionDrag::find_time_axis_view (TimeAxisView* t) const
540 int const N = _time_axis_views.size ();
541 while (i < N && _time_axis_views[i] != t) {
545 if (_time_axis_views[i] != t) {
552 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
553 : RegionDrag (e, i, p, v)
556 , _last_pointer_time_axis_view (0)
557 , _last_pointer_layer (0)
558 , _single_axis (false)
563 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
567 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
569 Drag::start_grab (event, cursor);
571 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
575 show_verbose_cursor_time (_last_frame_position);
577 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
579 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
580 assert(_last_pointer_time_axis_view >= 0);
581 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
586 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
588 /* compute the amount of pointer motion in frames, and where
589 the region would be if we moved it by that much.
591 *pending_region_position = adjusted_current_frame (event);
593 framepos_t sync_frame;
594 framecnt_t sync_offset;
597 sync_offset = _primary->region()->sync_offset (sync_dir);
599 /* we don't handle a sync point that lies before zero.
601 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
603 sync_frame = *pending_region_position + (sync_dir*sync_offset);
605 _editor->snap_to_with_modifier (sync_frame, event);
607 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
610 *pending_region_position = _last_frame_position;
613 if (*pending_region_position > max_framepos - _primary->region()->length()) {
614 *pending_region_position = _last_frame_position;
619 /* in locked edit mode, reverse the usual meaning of _x_constrained */
620 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
622 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
624 /* x movement since last time (in pixels) */
625 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
627 /* total x movement */
628 framecnt_t total_dx = *pending_region_position;
629 if (regions_came_from_canvas()) {
630 total_dx = total_dx - grab_frame ();
633 /* check that no regions have gone off the start of the session */
634 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
635 if ((i->view->region()->position() + total_dx) < 0) {
637 *pending_region_position = _last_frame_position;
648 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
654 const int tavsize = _time_axis_views.size();
655 const int dt = delta > 0 ? +1 : -1;
657 int target = start + delta - skip;
659 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
660 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
662 while (current >= 0 && current != target) {
664 if (current < 0 && dt < 0) {
667 if (current >= tavsize && dt > 0) {
670 if (current < 0 || current >= tavsize) {
674 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
675 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
679 if (distance_only && current == start + delta) {
687 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
689 if (_y_constrained) {
693 const int tavsize = _time_axis_views.size();
694 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
695 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
696 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
698 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
699 /* already in the drop zone */
700 if (delta_track >= 0) {
701 /* downward motion - OK if others are still not in the dropzone */
710 } else if (n >= tavsize) {
711 /* downward motion into drop zone. That's fine. */
715 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
716 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
717 /* not a track, or the wrong type */
721 double const l = i->layer + delta_layer;
723 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
724 mode to allow the user to place a region below another on layer 0.
726 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
727 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
728 If it has, the layers will be munged later anyway, so it's ok.
734 /* all regions being dragged are ok with this change */
738 struct DraggingViewSorter {
739 bool operator() (const DraggingView& a, const DraggingView& b) {
740 return a.time_axis_view < b.time_axis_view;
745 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
747 double delta_layer = 0;
748 int delta_time_axis_view = 0;
749 int current_pointer_time_axis_view = -1;
751 assert (!_views.empty ());
755 if (initially_vertical()) {
756 _y_constrained = false;
757 _x_constrained = true;
759 _y_constrained = true;
760 _x_constrained = false;
765 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
767 /* Find the TimeAxisView that the pointer is now over */
768 const double cur_y = current_pointer_y ();
769 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
770 TimeAxisView* tv = r.first;
772 if (!tv && cur_y < 0) {
773 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
777 /* find drop-zone y-position */
778 Coord last_track_bottom_edge;
779 last_track_bottom_edge = 0;
780 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
781 if (!(*t)->hidden()) {
782 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
787 if (tv && tv->view()) {
788 /* the mouse is over a track */
789 double layer = r.second;
791 if (first_move && tv->view()->layer_display() == Stacked) {
792 tv->view()->set_layer_display (Expanded);
795 /* Here's the current pointer position in terms of time axis view and layer */
796 current_pointer_time_axis_view = find_time_axis_view (tv);
797 assert(current_pointer_time_axis_view >= 0);
799 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
801 /* Work out the change in y */
803 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
804 if (!rtv || !rtv->is_track()) {
805 /* ignore busses early on. we can't move any regions on them */
806 } else if (_last_pointer_time_axis_view < 0) {
807 /* Was in the drop-zone, now over a track.
808 * Hence it must be an upward move (from the bottom)
810 * track_index is still -1, so delta must be set to
811 * move up the correct number of tracks from the bottom.
813 * This is necessary because steps may be skipped if
814 * the bottom-most track is not a valid target and/or
815 * if there are hidden tracks at the bottom.
816 * Hence the initial offset (_ddropzone) as well as the
817 * last valid pointer position (_pdropzone) need to be
818 * taken into account.
820 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
822 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
825 /* TODO needs adjustment per DraggingView,
827 * e.g. select one region on the top-layer of a track
828 * and one region which is at the bottom-layer of another track
831 * Indicated drop-zones and layering is wrong.
832 * and may infer additional layers on the target-track
833 * (depending how many layers the original track had).
835 * Or select two regions (different layers) on a same track,
836 * move across a non-layer track.. -> layering info is lost.
837 * on drop either of the regions may be on top.
839 * Proposed solution: screw it :) well,
840 * don't use delta_layer, use an absolute value
841 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
842 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
843 * 3) iterate over all DraggingView, find the one that is over the track with most layers
844 * 4) proportionally scale layer to layers available on target
846 delta_layer = current_pointer_layer - _last_pointer_layer;
849 /* for automation lanes, there is a TimeAxisView but no ->view()
850 * if (!tv) -> dropzone
852 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
853 /* Moving into the drop-zone.. */
854 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
855 /* delta_time_axis_view may not be sufficient to move into the DZ
856 * the mouse may enter it, but it may not be a valid move due to
859 * -> remember the delta needed to move into the dropzone
861 _ddropzone = delta_time_axis_view;
862 /* ..but subtract hidden tracks (or routes) at the bottom.
863 * we silently move mover them
865 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
866 - _time_axis_views.size();
868 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
869 /* move around inside the zone.
870 * This allows to move further down until all regions are in the zone.
872 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
873 assert(ptr_y >= last_track_bottom_edge);
874 assert(_ddropzone > 0);
876 /* calculate mouse position in 'tracks' below last track. */
877 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
878 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
880 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
882 delta_time_axis_view = dzpos - _pdropzone;
883 } else if (dzpos < _pdropzone && _ndropzone > 0) {
884 // move up inside the DZ
885 delta_time_axis_view = dzpos - _pdropzone;
889 /* Work out the change in x */
890 framepos_t pending_region_position;
891 double const x_delta = compute_x_delta (event, &pending_region_position);
892 _last_frame_position = pending_region_position;
894 /* calculate hidden tracks in current y-axis delta */
896 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
897 /* The mouse is more than one track below the dropzone.
898 * distance calculation is not needed (and would not work, either
899 * because the dropzone is "packed").
901 * Except when [partially] moving regions out of dropzone in a large step.
902 * (the mouse may or may not remain in the DZ)
903 * Hidden tracks at the bottom of the TAV need to be skipped.
905 * This also handles the case if the mouse entered the DZ
906 * in a large step (exessive delta), either due to fast-movement,
907 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
909 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
910 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
912 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
913 -_time_axis_views.size() - dt;
916 else if (_last_pointer_time_axis_view < 0) {
917 /* Moving out of the zone. Check for hidden tracks at the bottom. */
918 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
919 -_time_axis_views.size() - delta_time_axis_view;
921 /* calculate hidden tracks that are skipped by the pointer movement */
922 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
923 - _last_pointer_time_axis_view
924 - delta_time_axis_view;
927 /* Verify change in y */
928 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
929 /* this y movement is not allowed, so do no y movement this time */
930 delta_time_axis_view = 0;
935 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
936 /* haven't reached next snap point, and we're not switching
937 trackviews nor layers. nothing to do.
942 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
943 PlaylistDropzoneMap playlist_dropzone_map;
944 _ndropzone = 0; // number of elements currently in the dropzone
947 /* sort views by time_axis.
948 * This retains track order in the dropzone, regardless
949 * of actual selection order
951 _views.sort (DraggingViewSorter());
953 /* count number of distinct tracks of all regions
954 * being dragged, used for dropzone.
957 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
958 if (i->time_axis_view != prev_track) {
959 prev_track = i->time_axis_view;
965 _views.back().time_axis_view -
966 _views.front().time_axis_view;
968 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
969 - _views.back().time_axis_view;
971 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
975 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
977 RegionView* rv = i->view;
982 if (rv->region()->locked() || rv->region()->video_locked()) {
989 /* reparent the regionview into a group above all
993 ArdourCanvas::Item* rvg = rv->get_canvas_group();
994 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
995 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
996 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
997 /* move the item so that it continues to appear at the
998 same location now that its parent has changed.
1000 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1003 /* If we have moved tracks, we'll fudge the layer delta so that the
1004 region gets moved back onto layer 0 on its new track; this avoids
1005 confusion when dragging regions from non-zero layers onto different
1008 double this_delta_layer = delta_layer;
1009 if (delta_time_axis_view != 0) {
1010 this_delta_layer = - i->layer;
1013 int this_delta_time_axis_view = delta_time_axis_view;
1014 this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1016 int track_index = i->time_axis_view + this_delta_time_axis_view;
1017 assert(track_index >= 0);
1019 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1020 /* Track is in the Dropzone */
1022 i->time_axis_view = track_index;
1023 assert(i->time_axis_view >= (int) _time_axis_views.size());
1026 double yposition = 0;
1027 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1028 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1031 /* store index of each new playlist as a negative count, starting at -1 */
1033 if (pdz == playlist_dropzone_map.end()) {
1034 /* compute where this new track (which doesn't exist yet) will live
1037 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1039 /* How high is this region view ? */
1041 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1042 ArdourCanvas::Rect bbox;
1045 bbox = obbox.get ();
1048 last_track_bottom_edge += bbox.height();
1050 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1053 yposition = pdz->second;
1056 /* values are zero or negative, hence the use of min() */
1057 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1062 /* The TimeAxisView that this region is now over */
1063 TimeAxisView* current_tv = _time_axis_views[track_index];
1065 /* Ensure it is moved from stacked -> expanded if appropriate */
1066 if (current_tv->view()->layer_display() == Stacked) {
1067 current_tv->view()->set_layer_display (Expanded);
1070 /* We're only allowed to go -ve in layer on Expanded views */
1071 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1072 this_delta_layer = - i->layer;
1076 rv->set_height (current_tv->view()->child_height ());
1078 /* Update show/hidden status as the region view may have come from a hidden track,
1079 or have moved to one.
1081 if (current_tv->hidden ()) {
1082 rv->get_canvas_group()->hide ();
1084 rv->get_canvas_group()->show ();
1087 /* Update the DraggingView */
1088 i->time_axis_view = track_index;
1089 i->layer += this_delta_layer;
1092 _editor->mouse_brush_insert_region (rv, pending_region_position);
1096 /* Get the y coordinate of the top of the track that this region is now over */
1097 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1099 /* And adjust for the layer that it should be on */
1100 StreamView* cv = current_tv->view ();
1101 switch (cv->layer_display ()) {
1105 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1108 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1112 /* need to get the parent of the regionview
1113 * canvas group and get its position in
1114 * equivalent coordinate space as the trackview
1115 * we are now dragging over.
1118 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1123 /* Now move the region view */
1124 rv->move (x_delta, y_delta);
1126 } /* foreach region */
1128 _total_x_delta += x_delta;
1130 if (x_delta != 0 && !_brushing) {
1131 show_verbose_cursor_time (_last_frame_position);
1134 /* keep track of pointer movement */
1136 /* the pointer is currently over a time axis view */
1138 if (_last_pointer_time_axis_view < 0) {
1139 /* last motion event was not over a time axis view
1140 * or last y-movement out of the dropzone was not valid
1143 if (delta_time_axis_view < 0) {
1144 /* in the drop zone, moving up */
1146 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1147 * We do not use negative _last_pointer_time_axis_view because
1148 * the dropzone is "packed" (the actual track offset is ignored)
1150 * As opposed to the actual number
1151 * of elements in the dropzone (_ndropzone)
1152 * _pdropzone is not constrained. This is necessary
1153 * to allow moving multiple regions with y-distance
1156 * There can be 0 elements in the dropzone,
1157 * even though the drag-pointer is inside the DZ.
1160 * [ Audio-track, Midi-track, Audio-track, DZ ]
1161 * move regions from both audio tracks at the same time into the
1162 * DZ by grabbing the region in the bottom track.
1164 assert(current_pointer_time_axis_view >= 0);
1165 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1169 /* only move out of the zone if the movement is OK */
1170 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1171 assert(delta_time_axis_view < 0);
1172 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1173 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1174 * the current position can be calculated as follows:
1176 // a well placed oofus attack can still throw this off.
1177 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1178 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1181 /* last motion event was also over a time axis view */
1182 _last_pointer_time_axis_view += delta_time_axis_view;
1183 assert(_last_pointer_time_axis_view >= 0);
1188 /* the pointer is not over a time axis view */
1189 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1190 _pdropzone += delta_time_axis_view - delta_skip;
1191 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1194 _last_pointer_layer += delta_layer;
1198 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1200 if (_copy && first_move) {
1202 if (_x_constrained) {
1203 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1205 _editor->begin_reversible_command (Operations::region_copy);
1208 /* duplicate the regionview(s) and region(s) */
1210 list<DraggingView> new_regionviews;
1212 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1214 RegionView* rv = i->view;
1215 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1216 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1218 const boost::shared_ptr<const Region> original = rv->region();
1219 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1220 region_copy->set_position (original->position());
1221 /* need to set this so that the drop zone code can work. This doesn't
1222 actually put the region into the playlist, but just sets a weak pointer
1225 region_copy->set_playlist (original->playlist());
1229 boost::shared_ptr<AudioRegion> audioregion_copy
1230 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1232 nrv = new AudioRegionView (*arv, audioregion_copy);
1234 boost::shared_ptr<MidiRegion> midiregion_copy
1235 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1236 nrv = new MidiRegionView (*mrv, midiregion_copy);
1241 nrv->get_canvas_group()->show ();
1242 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1244 /* swap _primary to the copy */
1246 if (rv == _primary) {
1250 /* ..and deselect the one we copied */
1252 rv->set_selected (false);
1255 if (!new_regionviews.empty()) {
1257 /* reflect the fact that we are dragging the copies */
1259 _views = new_regionviews;
1261 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1264 } else if (!_copy && first_move) {
1266 if (_x_constrained) {
1267 _editor->begin_reversible_command (_("fixed time region drag"));
1269 _editor->begin_reversible_command (Operations::region_drag);
1273 RegionMotionDrag::motion (event, first_move);
1277 RegionMotionDrag::finished (GdkEvent *, bool)
1279 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1280 if (!(*i)->view()) {
1284 if ((*i)->view()->layer_display() == Expanded) {
1285 (*i)->view()->set_layer_display (Stacked);
1291 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1293 RegionMotionDrag::finished (ev, movement_occurred);
1295 if (!movement_occurred) {
1299 if (was_double_click() && !_views.empty()) {
1300 DraggingView dv = _views.front();
1301 dv.view->show_region_editor ();
1308 /* reverse this here so that we have the correct logic to finalize
1312 if (Config->get_edit_mode() == Lock) {
1313 _x_constrained = !_x_constrained;
1316 assert (!_views.empty ());
1318 /* We might have hidden region views so that they weren't visible during the drag
1319 (when they have been reparented). Now everything can be shown again, as region
1320 views are back in their track parent groups.
1322 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1323 i->view->get_canvas_group()->show ();
1326 bool const changed_position = (_last_frame_position != _primary->region()->position());
1327 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1328 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1348 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1352 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1354 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1359 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1360 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1361 uint32_t output_chan = region->n_channels();
1362 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1363 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1365 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name());
1366 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1368 rtav->set_height (original->current_height());
1372 ChanCount one_midi_port (DataType::MIDI, 1);
1373 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1374 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1375 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1377 rtav->set_height (original->current_height());
1382 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1388 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1390 RegionSelection new_views;
1391 PlaylistSet modified_playlists;
1392 RouteTimeAxisView* new_time_axis_view = 0;
1395 /* all changes were made during motion event handlers */
1397 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1401 _editor->commit_reversible_command ();
1405 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1406 PlaylistMapping playlist_mapping;
1408 /* insert the regions into their new playlists */
1409 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1411 RouteTimeAxisView* dest_rtv = 0;
1413 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1419 if (changed_position && !_x_constrained) {
1420 where = i->view->region()->position() - drag_delta;
1422 where = i->view->region()->position();
1425 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1426 /* dragged to drop zone */
1428 PlaylistMapping::iterator pm;
1430 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1431 /* first region from this original playlist: create a new track */
1432 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1433 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1434 dest_rtv = new_time_axis_view;
1436 /* we already created a new track for regions from this playlist, use it */
1437 dest_rtv = pm->second;
1440 /* destination time axis view is the one we dragged to */
1441 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1444 if (dest_rtv != 0) {
1445 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1446 if (new_view != 0) {
1447 new_views.push_back (new_view);
1451 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1452 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1455 list<DraggingView>::const_iterator next = i;
1461 /* If we've created new regions either by copying or moving
1462 to a new track, we want to replace the old selection with the new ones
1465 if (new_views.size() > 0) {
1466 _editor->selection->set (new_views);
1469 /* write commands for the accumulated diffs for all our modified playlists */
1470 add_stateful_diff_commands_for_playlists (modified_playlists);
1472 _editor->commit_reversible_command ();
1476 RegionMoveDrag::finished_no_copy (
1477 bool const changed_position,
1478 bool const changed_tracks,
1479 framecnt_t const drag_delta
1482 RegionSelection new_views;
1483 PlaylistSet modified_playlists;
1484 PlaylistSet frozen_playlists;
1485 set<RouteTimeAxisView*> views_to_update;
1486 RouteTimeAxisView* new_time_axis_view = 0;
1489 /* all changes were made during motion event handlers */
1490 _editor->commit_reversible_command ();
1494 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1495 PlaylistMapping playlist_mapping;
1497 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1499 RegionView* rv = i->view;
1500 RouteTimeAxisView* dest_rtv = 0;
1502 if (rv->region()->locked() || rv->region()->video_locked()) {
1507 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1508 /* dragged to drop zone */
1510 PlaylistMapping::iterator pm;
1512 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1513 /* first region from this original playlist: create a new track */
1514 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1515 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1516 dest_rtv = new_time_axis_view;
1518 /* we already created a new track for regions from this playlist, use it */
1519 dest_rtv = pm->second;
1523 /* destination time axis view is the one we dragged to */
1524 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1529 double const dest_layer = i->layer;
1531 views_to_update.insert (dest_rtv);
1535 if (changed_position && !_x_constrained) {
1536 where = rv->region()->position() - drag_delta;
1538 where = rv->region()->position();
1541 if (changed_tracks) {
1543 /* insert into new playlist */
1545 RegionView* new_view = insert_region_into_playlist (
1546 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1549 if (new_view == 0) {
1554 new_views.push_back (new_view);
1556 /* remove from old playlist */
1558 /* the region that used to be in the old playlist is not
1559 moved to the new one - we use a copy of it. as a result,
1560 any existing editor for the region should no longer be
1563 rv->hide_region_editor();
1566 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1570 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1572 /* this movement may result in a crossfade being modified, or a layering change,
1573 so we need to get undo data from the playlist as well as the region.
1576 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1578 playlist->clear_changes ();
1581 rv->region()->clear_changes ();
1584 motion on the same track. plonk the previously reparented region
1585 back to its original canvas group (its streamview).
1586 No need to do anything for copies as they are fake regions which will be deleted.
1589 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1590 rv->get_canvas_group()->set_y_position (i->initial_y);
1593 /* just change the model */
1594 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1595 playlist->set_layer (rv->region(), dest_layer);
1598 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1600 r = frozen_playlists.insert (playlist);
1603 playlist->freeze ();
1606 rv->region()->set_position (where);
1608 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1611 if (changed_tracks) {
1613 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1614 was selected in all of them, then removing it from a playlist will have removed all
1615 trace of it from _views (i.e. there were N regions selected, we removed 1,
1616 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1617 corresponding regionview, and _views is now empty).
1619 This could have invalidated any and all iterators into _views.
1621 The heuristic we use here is: if the region selection is empty, break out of the loop
1622 here. if the region selection is not empty, then restart the loop because we know that
1623 we must have removed at least the region(view) we've just been working on as well as any
1624 that we processed on previous iterations.
1626 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1627 we can just iterate.
1631 if (_views.empty()) {
1642 /* If we've created new regions either by copying or moving
1643 to a new track, we want to replace the old selection with the new ones
1646 if (new_views.size() > 0) {
1647 _editor->selection->set (new_views);
1650 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1654 /* write commands for the accumulated diffs for all our modified playlists */
1655 add_stateful_diff_commands_for_playlists (modified_playlists);
1657 _editor->commit_reversible_command ();
1659 /* We have futzed with the layering of canvas items on our streamviews.
1660 If any region changed layer, this will have resulted in the stream
1661 views being asked to set up their region views, and all will be well.
1662 If not, we might now have badly-ordered region views. Ask the StreamViews
1663 involved to sort themselves out, just in case.
1666 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1667 (*i)->view()->playlist_layered ((*i)->track ());
1671 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1672 * @param region Region to remove.
1673 * @param playlist playlist To remove from.
1674 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1675 * that clear_changes () is only called once per playlist.
1678 RegionMoveDrag::remove_region_from_playlist (
1679 boost::shared_ptr<Region> region,
1680 boost::shared_ptr<Playlist> playlist,
1681 PlaylistSet& modified_playlists
1684 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1687 playlist->clear_changes ();
1690 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1694 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1695 * clearing the playlist's diff history first if necessary.
1696 * @param region Region to insert.
1697 * @param dest_rtv Destination RouteTimeAxisView.
1698 * @param dest_layer Destination layer.
1699 * @param where Destination position.
1700 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1701 * that clear_changes () is only called once per playlist.
1702 * @return New RegionView, or 0 if no insert was performed.
1705 RegionMoveDrag::insert_region_into_playlist (
1706 boost::shared_ptr<Region> region,
1707 RouteTimeAxisView* dest_rtv,
1710 PlaylistSet& modified_playlists
1713 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1714 if (!dest_playlist) {
1718 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1719 _new_region_view = 0;
1720 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1722 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1723 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1725 dest_playlist->clear_changes ();
1728 dest_playlist->add_region (region, where);
1730 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1731 dest_playlist->set_layer (region, dest_layer);
1736 assert (_new_region_view);
1738 return _new_region_view;
1742 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1744 _new_region_view = rv;
1748 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1750 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1751 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1753 _editor->session()->add_command (c);
1762 RegionMoveDrag::aborted (bool movement_occurred)
1766 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1767 list<DraggingView>::const_iterator next = i;
1776 RegionMotionDrag::aborted (movement_occurred);
1781 RegionMotionDrag::aborted (bool)
1783 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1785 StreamView* sview = (*i)->view();
1788 if (sview->layer_display() == Expanded) {
1789 sview->set_layer_display (Stacked);
1794 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1795 RegionView* rv = i->view;
1796 TimeAxisView* tv = &(rv->get_time_axis_view ());
1797 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1799 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1800 rv->get_canvas_group()->set_y_position (0);
1802 rv->move (-_total_x_delta, 0);
1803 rv->set_height (rtv->view()->child_height ());
1807 /** @param b true to brush, otherwise false.
1808 * @param c true to make copies of the regions being moved, otherwise false.
1810 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1811 : RegionMotionDrag (e, i, p, v, b)
1814 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1817 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1818 if (rtv && rtv->is_track()) {
1819 speed = rtv->track()->speed ();
1822 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1826 RegionMoveDrag::setup_pointer_frame_offset ()
1828 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1831 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1832 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1834 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1836 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1837 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1839 _primary = v->view()->create_region_view (r, false, false);
1841 _primary->get_canvas_group()->show ();
1842 _primary->set_position (pos, 0);
1843 _views.push_back (DraggingView (_primary, this, v));
1845 _last_frame_position = pos;
1847 _item = _primary->get_canvas_group ();
1851 RegionInsertDrag::finished (GdkEvent *, bool)
1853 int pos = _views.front().time_axis_view;
1854 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1856 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1858 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1859 _primary->get_canvas_group()->set_y_position (0);
1861 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1863 _editor->begin_reversible_command (Operations::insert_region);
1864 playlist->clear_changes ();
1865 playlist->add_region (_primary->region (), _last_frame_position);
1867 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1868 if (Config->get_edit_mode() == Ripple) {
1869 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1872 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1873 _editor->commit_reversible_command ();
1881 RegionInsertDrag::aborted (bool)
1888 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1889 : RegionMoveDrag (e, i, p, v, false, false)
1891 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1894 struct RegionSelectionByPosition {
1895 bool operator() (RegionView*a, RegionView* b) {
1896 return a->region()->position () < b->region()->position();
1901 RegionSpliceDrag::motion (GdkEvent* event, bool)
1903 /* Which trackview is this ? */
1905 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1906 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1908 /* The region motion is only processed if the pointer is over
1912 if (!tv || !tv->is_track()) {
1913 /* To make sure we hide the verbose canvas cursor when the mouse is
1914 not held over an audio track.
1916 _editor->verbose_cursor()->hide ();
1919 _editor->verbose_cursor()->show ();
1924 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1930 RegionSelection copy;
1931 _editor->selection->regions.by_position(copy);
1933 framepos_t const pf = adjusted_current_frame (event);
1935 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1937 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1943 boost::shared_ptr<Playlist> playlist;
1945 if ((playlist = atv->playlist()) == 0) {
1949 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1954 if (pf < (*i)->region()->last_frame() + 1) {
1958 if (pf > (*i)->region()->first_frame()) {
1964 playlist->shuffle ((*i)->region(), dir);
1969 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1971 RegionMoveDrag::finished (event, movement_occurred);
1975 RegionSpliceDrag::aborted (bool)
1985 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1988 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1990 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1991 RegionSelection to_ripple;
1992 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1993 if ((*i)->position() >= where) {
1994 to_ripple.push_back (rtv->view()->find_view(*i));
1998 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1999 if (!exclude.contains (*i)) {
2000 // the selection has already been added to _views
2002 if (drag_in_progress) {
2003 // do the same things that RegionMotionDrag::motion does when
2004 // first_move is true, for the region views that we're adding
2005 // to _views this time
2008 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2009 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2010 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2011 rvg->reparent (_editor->_drag_motion_group);
2013 // we only need to move in the y direction
2014 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2019 _views.push_back (DraggingView (*i, this, tav));
2025 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2028 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2029 // we added all the regions after the selection
2031 std::list<DraggingView>::iterator to_erase = i++;
2032 if (!_editor->selection->regions.contains (to_erase->view)) {
2033 // restore the non-selected regions to their original playlist & positions,
2034 // and then ripple them back by the length of the regions that were dragged away
2035 // do the same things as RegionMotionDrag::aborted
2037 RegionView *rv = to_erase->view;
2038 TimeAxisView* tv = &(rv->get_time_axis_view ());
2039 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2042 // plonk them back onto their own track
2043 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2044 rv->get_canvas_group()->set_y_position (0);
2048 // move the underlying region to match the view
2049 rv->region()->set_position (rv->region()->position() + amount);
2051 // restore the view to match the underlying region's original position
2052 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2055 rv->set_height (rtv->view()->child_height ());
2056 _views.erase (to_erase);
2062 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2064 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2066 return allow_moves_across_tracks;
2074 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2075 : RegionMoveDrag (e, i, p, v, false, false)
2077 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2078 // compute length of selection
2079 RegionSelection selected_regions = _editor->selection->regions;
2080 selection_length = selected_regions.end_frame() - selected_regions.start();
2082 // we'll only allow dragging to another track in ripple mode if all the regions
2083 // being dragged start off on the same track
2084 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2087 exclude = new RegionList;
2088 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2089 exclude->push_back((*i)->region());
2092 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2093 RegionSelection copy;
2094 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2096 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2097 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2099 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2100 // find ripple start point on each applicable playlist
2101 RegionView *first_selected_on_this_track = NULL;
2102 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2103 if ((*i)->region()->playlist() == (*pi)) {
2104 // region is on this playlist - it's the first, because they're sorted
2105 first_selected_on_this_track = *i;
2109 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2110 add_all_after_to_views (
2111 &first_selected_on_this_track->get_time_axis_view(),
2112 first_selected_on_this_track->region()->position(),
2113 selected_regions, false);
2116 if (allow_moves_across_tracks) {
2117 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2125 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2127 /* Which trackview is this ? */
2129 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2130 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2132 /* The region motion is only processed if the pointer is over
2136 if (!tv || !tv->is_track()) {
2137 /* To make sure we hide the verbose canvas cursor when the mouse is
2138 not held over an audiotrack.
2140 _editor->verbose_cursor()->hide ();
2144 framepos_t where = adjusted_current_frame (event);
2145 assert (where >= 0);
2147 double delta = compute_x_delta (event, &after);
2149 framecnt_t amount = _editor->pixel_to_sample (delta);
2151 if (allow_moves_across_tracks) {
2152 // all the originally selected regions were on the same track
2154 framecnt_t adjust = 0;
2155 if (prev_tav && tv != prev_tav) {
2156 // dragged onto a different track
2157 // remove the unselected regions from _views, restore them to their original positions
2158 // and add the regions after the drop point on the new playlist to _views instead.
2159 // undo the effect of rippling the previous playlist, and include the effect of removing
2160 // the dragged region(s) from this track
2162 remove_unselected_from_views (prev_amount, false);
2163 // ripple previous playlist according to the regions that have been removed onto the new playlist
2164 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2167 // move just the selected regions
2168 RegionMoveDrag::motion(event, first_move);
2170 // ensure that the ripple operation on the new playlist inserts selection_length time
2171 adjust = selection_length;
2172 // ripple the new current playlist
2173 tv->playlist()->ripple (where, amount+adjust, exclude);
2175 // add regions after point where drag entered this track to subsequent ripples
2176 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2179 // motion on same track
2180 RegionMoveDrag::motion(event, first_move);
2184 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2185 prev_position = where;
2187 // selection encompasses multiple tracks - just drag
2188 // cross-track drags are forbidden
2189 RegionMoveDrag::motion(event, first_move);
2192 if (!_x_constrained) {
2193 prev_amount += amount;
2196 _last_frame_position = after;
2200 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2202 if (!movement_occurred) {
2206 if (was_double_click() && !_views.empty()) {
2207 DraggingView dv = _views.front();
2208 dv.view->show_region_editor ();
2215 _editor->begin_reversible_command(_("Ripple drag"));
2217 // remove the regions being rippled from the dragging view, updating them to
2218 // their new positions
2219 remove_unselected_from_views (prev_amount, true);
2221 if (allow_moves_across_tracks) {
2223 // if regions were dragged across tracks, we've rippled any later
2224 // regions on the track the regions were dragged off, so we need
2225 // to add the original track to the undo record
2226 orig_tav->playlist()->clear_changes();
2227 vector<Command*> cmds;
2228 orig_tav->playlist()->rdiff (cmds);
2229 _editor->session()->add_commands (cmds);
2231 if (prev_tav && prev_tav != orig_tav) {
2232 prev_tav->playlist()->clear_changes();
2233 vector<Command*> cmds;
2234 prev_tav->playlist()->rdiff (cmds);
2235 _editor->session()->add_commands (cmds);
2238 // selection spanned multiple tracks - all will need adding to undo record
2240 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2241 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2243 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2244 (*pi)->clear_changes();
2245 vector<Command*> cmds;
2246 (*pi)->rdiff (cmds);
2247 _editor->session()->add_commands (cmds);
2251 // other modified playlists are added to undo by RegionMoveDrag::finished()
2252 RegionMoveDrag::finished (event, movement_occurred);
2253 _editor->commit_reversible_command();
2257 RegionRippleDrag::aborted (bool movement_occurred)
2259 RegionMoveDrag::aborted (movement_occurred);
2264 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2266 _view (dynamic_cast<MidiTimeAxisView*> (v))
2268 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2274 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2277 _region = add_midi_region (_view);
2278 _view->playlist()->freeze ();
2281 framepos_t const f = adjusted_current_frame (event);
2282 if (f < grab_frame()) {
2283 _region->set_position (f);
2286 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2287 so that if this region is duplicated, its duplicate starts on
2288 a snap point rather than 1 frame after a snap point. Otherwise things get
2289 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2290 place snapped notes at the start of the region.
2293 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2294 _region->set_length (len < 1 ? 1 : len);
2300 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2302 if (!movement_occurred) {
2303 add_midi_region (_view);
2305 _view->playlist()->thaw ();
2310 RegionCreateDrag::aborted (bool)
2313 _view->playlist()->thaw ();
2319 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2323 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2327 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2329 Gdk::Cursor* cursor;
2330 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2332 float x_fraction = cnote->mouse_x_fraction ();
2334 if (x_fraction > 0.0 && x_fraction < 0.25) {
2335 cursor = _editor->cursors()->left_side_trim;
2338 cursor = _editor->cursors()->right_side_trim;
2342 Drag::start_grab (event, cursor);
2344 region = &cnote->region_view();
2348 if (event->motion.state & Keyboard::PrimaryModifier) {
2354 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2356 if (ms.size() > 1) {
2357 /* has to be relative, may make no sense otherwise */
2361 /* select this note; if it is already selected, preserve the existing selection,
2362 otherwise make this note the only one selected.
2364 region->note_selected (cnote, cnote->selected ());
2366 _editor->begin_reversible_command (_("resize notes"));
2368 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2369 MidiRegionSelection::iterator next;
2372 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2374 mrv->begin_resizing (at_front);
2381 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
2383 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2384 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2385 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2387 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2389 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2395 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
2397 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2398 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2399 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2401 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2403 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2407 _editor->commit_reversible_command ();
2411 NoteResizeDrag::aborted (bool)
2413 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2414 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2415 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2417 mrv->abort_resizing ();
2422 AVDraggingView::AVDraggingView (RegionView* v)
2425 initial_position = v->region()->position ();
2428 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2431 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2434 TrackViewList empty;
2436 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2437 std::list<RegionView*> views = rs.by_layer();
2439 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2440 RegionView* rv = (*i);
2441 if (!rv->region()->video_locked()) {
2444 _views.push_back (AVDraggingView (rv));
2449 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2451 Drag::start_grab (event);
2452 if (_editor->session() == 0) {
2456 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2457 _max_backwards_drag = (
2458 ARDOUR_UI::instance()->video_timeline->get_duration()
2459 + ARDOUR_UI::instance()->video_timeline->get_offset()
2460 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2463 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2464 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2465 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2468 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2471 Timecode::Time timecode;
2472 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2473 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);
2474 show_verbose_cursor_text (buf);
2478 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2480 if (_editor->session() == 0) {
2483 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2487 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2488 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2490 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2491 dt = - _max_backwards_drag;
2494 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2495 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2497 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2498 RegionView* rv = i->view;
2499 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2502 rv->region()->clear_changes ();
2503 rv->region()->suspend_property_changes();
2505 rv->region()->set_position(i->initial_position + dt);
2506 rv->region_changed(ARDOUR::Properties::position);
2509 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2510 Timecode::Time timecode;
2511 Timecode::Time timediff;
2513 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2514 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2515 snprintf (buf, sizeof (buf),
2516 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2517 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2518 , _("Video Start:"),
2519 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2521 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2523 show_verbose_cursor_text (buf);
2527 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2529 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2533 if (!movement_occurred || ! _editor->session()) {
2537 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2539 _editor->begin_reversible_command (_("Move Video"));
2541 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2542 ARDOUR_UI::instance()->video_timeline->save_undo();
2543 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2544 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2546 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2547 i->view->drag_end();
2548 i->view->region()->resume_property_changes ();
2550 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2553 _editor->session()->maybe_update_session_range(
2554 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2555 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2559 _editor->commit_reversible_command ();
2563 VideoTimeLineDrag::aborted (bool)
2565 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2568 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2569 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2571 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2572 i->view->region()->resume_property_changes ();
2573 i->view->region()->set_position(i->initial_position);
2577 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2578 : RegionDrag (e, i, p, v)
2579 , _preserve_fade_anchor (preserve_fade_anchor)
2580 , _jump_position_when_done (false)
2582 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2586 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2589 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2590 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2592 if (tv && tv->is_track()) {
2593 speed = tv->track()->speed();
2596 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2597 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2598 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2600 framepos_t const pf = adjusted_current_frame (event);
2602 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2603 /* Move the contents of the region around without changing the region bounds */
2604 _operation = ContentsTrim;
2605 Drag::start_grab (event, _editor->cursors()->trimmer);
2607 /* These will get overridden for a point trim.*/
2608 if (pf < (region_start + region_length/2)) {
2609 /* closer to front */
2610 _operation = StartTrim;
2612 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2613 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2615 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2619 _operation = EndTrim;
2620 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2621 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2623 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2628 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2629 _jump_position_when_done = true;
2632 switch (_operation) {
2634 show_verbose_cursor_time (region_start);
2635 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2636 i->view->trim_front_starting ();
2640 show_verbose_cursor_time (region_end);
2643 show_verbose_cursor_time (pf);
2647 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2648 i->view->region()->suspend_property_changes ();
2653 TrimDrag::motion (GdkEvent* event, bool first_move)
2655 RegionView* rv = _primary;
2658 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2659 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2660 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2661 frameoffset_t frame_delta = 0;
2663 if (tv && tv->is_track()) {
2664 speed = tv->track()->speed();
2667 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2673 switch (_operation) {
2675 trim_type = "Region start trim";
2678 trim_type = "Region end trim";
2681 trim_type = "Region content trim";
2688 _editor->begin_reversible_command (trim_type);
2690 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2691 RegionView* rv = i->view;
2692 rv->enable_display (false);
2693 rv->region()->playlist()->clear_owned_changes ();
2695 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2698 arv->temporarily_hide_envelope ();
2702 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2703 insert_result = _editor->motion_frozen_playlists.insert (pl);
2705 if (insert_result.second) {
2711 bool non_overlap_trim = false;
2713 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2714 non_overlap_trim = true;
2717 /* contstrain trim to fade length */
2718 if (_preserve_fade_anchor) {
2719 switch (_operation) {
2721 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2722 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2724 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2725 if (ar->locked()) continue;
2726 framecnt_t len = ar->fade_in()->back()->when;
2727 if (len < dt) dt = min(dt, len);
2731 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2732 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2734 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2735 if (ar->locked()) continue;
2736 framecnt_t len = ar->fade_out()->back()->when;
2737 if (len < -dt) dt = max(dt, -len);
2746 switch (_operation) {
2748 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2749 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2750 if (changed && _preserve_fade_anchor) {
2751 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2753 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2754 framecnt_t len = ar->fade_in()->back()->when;
2755 framecnt_t diff = ar->first_frame() - i->initial_position;
2756 framepos_t new_length = len - diff;
2757 i->anchored_fade_length = min (ar->length(), new_length);
2758 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2759 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2766 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2767 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2768 if (changed && _preserve_fade_anchor) {
2769 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2771 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2772 framecnt_t len = ar->fade_out()->back()->when;
2773 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2774 framepos_t new_length = len + diff;
2775 i->anchored_fade_length = min (ar->length(), new_length);
2776 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2777 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2785 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2787 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2788 i->view->move_contents (frame_delta);
2794 switch (_operation) {
2796 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2799 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2802 // show_verbose_cursor_time (frame_delta);
2809 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2811 if (movement_occurred) {
2812 motion (event, false);
2814 if (_operation == StartTrim) {
2815 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2817 /* This must happen before the region's StatefulDiffCommand is created, as it may
2818 `correct' (ahem) the region's _start from being negative to being zero. It
2819 needs to be zero in the undo record.
2821 i->view->trim_front_ending ();
2823 if (_preserve_fade_anchor) {
2824 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2826 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2827 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2828 ar->set_fade_in_length(i->anchored_fade_length);
2829 ar->set_fade_in_active(true);
2832 if (_jump_position_when_done) {
2833 i->view->region()->set_position (i->initial_position);
2836 } else if (_operation == EndTrim) {
2837 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2838 if (_preserve_fade_anchor) {
2839 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2841 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2842 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2843 ar->set_fade_out_length(i->anchored_fade_length);
2844 ar->set_fade_out_active(true);
2847 if (_jump_position_when_done) {
2848 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2853 if (!_views.empty()) {
2854 if (_operation == StartTrim) {
2855 _editor->maybe_locate_with_edit_preroll(
2856 _views.begin()->view->region()->position());
2858 if (_operation == EndTrim) {
2859 _editor->maybe_locate_with_edit_preroll(
2860 _views.begin()->view->region()->position() +
2861 _views.begin()->view->region()->length());
2865 if (!_editor->selection->selected (_primary)) {
2866 _primary->thaw_after_trim ();
2869 set<boost::shared_ptr<Playlist> > diffed_playlists;
2871 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2872 i->view->thaw_after_trim ();
2873 i->view->enable_display (true);
2875 /* Trimming one region may affect others on the playlist, so we need
2876 to get undo Commands from the whole playlist rather than just the
2877 region. Use diffed_playlists to make sure we don't diff a given
2878 playlist more than once.
2880 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2881 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2882 vector<Command*> cmds;
2884 _editor->session()->add_commands (cmds);
2885 diffed_playlists.insert (p);
2890 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2894 _editor->motion_frozen_playlists.clear ();
2895 _editor->commit_reversible_command();
2898 /* no mouse movement */
2899 _editor->point_trim (event, adjusted_current_frame (event));
2902 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2903 if (_operation == StartTrim) {
2904 i->view->trim_front_ending ();
2907 i->view->region()->resume_property_changes ();
2912 TrimDrag::aborted (bool movement_occurred)
2914 /* Our motion method is changing model state, so use the Undo system
2915 to cancel. Perhaps not ideal, as this will leave an Undo point
2916 behind which may be slightly odd from the user's point of view.
2921 if (movement_occurred) {
2925 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2926 i->view->region()->resume_property_changes ();
2931 TrimDrag::setup_pointer_frame_offset ()
2933 list<DraggingView>::iterator i = _views.begin ();
2934 while (i != _views.end() && i->view != _primary) {
2938 if (i == _views.end()) {
2942 switch (_operation) {
2944 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2947 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2954 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2958 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2959 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2964 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2966 Drag::start_grab (event, cursor);
2967 show_verbose_cursor_time (adjusted_current_frame(event));
2971 MeterMarkerDrag::setup_pointer_frame_offset ()
2973 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2977 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2979 if (!_marker->meter().movable()) {
2985 // create a dummy marker for visual representation of moving the
2986 // section, because whether its a copy or not, we're going to
2987 // leave or lose the original marker (leave if its a copy; lose if its
2988 // not, because we'll remove it from the map).
2990 MeterSection section (_marker->meter());
2992 if (!section.movable()) {
2997 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2999 _marker = new MeterMarker (
3001 *_editor->meter_group,
3002 ARDOUR_UI::config()->color ("meter marker"),
3004 *new MeterSection (_marker->meter())
3007 /* use the new marker for the grab */
3008 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3011 TempoMap& map (_editor->session()->tempo_map());
3012 /* get current state */
3013 before_state = &map.get_state();
3014 /* remove the section while we drag it */
3015 map.remove_meter (section, true);
3019 framepos_t const pf = adjusted_current_frame (event);
3021 _marker->set_position (pf);
3022 show_verbose_cursor_time (pf);
3026 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3028 if (!movement_occurred) {
3029 if (was_double_click()) {
3030 _editor->edit_meter_marker (*_marker);
3035 if (!_marker->meter().movable()) {
3039 motion (event, false);
3041 Timecode::BBT_Time when;
3043 TempoMap& map (_editor->session()->tempo_map());
3044 map.bbt_time (last_pointer_frame(), when);
3046 if (_copy == true) {
3047 _editor->begin_reversible_command (_("copy meter mark"));
3048 XMLNode &before = map.get_state();
3049 map.add_meter (_marker->meter(), when);
3050 XMLNode &after = map.get_state();
3051 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3052 _editor->commit_reversible_command ();
3055 _editor->begin_reversible_command (_("move meter mark"));
3057 /* we removed it before, so add it back now */
3059 map.add_meter (_marker->meter(), when);
3060 XMLNode &after = map.get_state();
3061 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3062 _editor->commit_reversible_command ();
3065 // delete the dummy marker we used for visual representation while moving.
3066 // a new visual marker will show up automatically.
3071 MeterMarkerDrag::aborted (bool moved)
3073 _marker->set_position (_marker->meter().frame ());
3076 TempoMap& map (_editor->session()->tempo_map());
3077 /* we removed it before, so add it back now */
3078 map.add_meter (_marker->meter(), _marker->meter().frame());
3079 // delete the dummy marker we used for visual representation while moving.
3080 // a new visual marker will show up automatically.
3085 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3089 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3091 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3096 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3098 Drag::start_grab (event, cursor);
3099 show_verbose_cursor_time (adjusted_current_frame (event));
3103 TempoMarkerDrag::setup_pointer_frame_offset ()
3105 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3109 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3111 if (!_marker->tempo().movable()) {
3117 // create a dummy marker for visual representation of moving the
3118 // section, because whether its a copy or not, we're going to
3119 // leave or lose the original marker (leave if its a copy; lose if its
3120 // not, because we'll remove it from the map).
3122 // create a dummy marker for visual representation of moving the copy.
3123 // The actual copying is not done before we reach the finish callback.
3126 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3128 TempoSection section (_marker->tempo());
3130 _marker = new TempoMarker (
3132 *_editor->tempo_group,
3133 ARDOUR_UI::config()->color ("tempo marker"),
3135 *new TempoSection (_marker->tempo())
3138 /* use the new marker for the grab */
3139 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3142 TempoMap& map (_editor->session()->tempo_map());
3143 /* get current state */
3144 before_state = &map.get_state();
3145 /* remove the section while we drag it */
3146 map.remove_tempo (section, true);
3150 framepos_t const pf = adjusted_current_frame (event);
3151 _marker->set_position (pf);
3152 show_verbose_cursor_time (pf);
3156 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3158 if (!movement_occurred) {
3159 if (was_double_click()) {
3160 _editor->edit_tempo_marker (*_marker);
3165 if (!_marker->tempo().movable()) {
3169 motion (event, false);
3171 TempoMap& map (_editor->session()->tempo_map());
3172 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3173 Timecode::BBT_Time when;
3175 map.bbt_time (beat_time, when);
3177 if (_copy == true) {
3178 _editor->begin_reversible_command (_("copy tempo mark"));
3179 XMLNode &before = map.get_state();
3180 map.add_tempo (_marker->tempo(), when);
3181 XMLNode &after = map.get_state();
3182 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3183 _editor->commit_reversible_command ();
3186 _editor->begin_reversible_command (_("move tempo mark"));
3187 /* we removed it before, so add it back now */
3188 map.add_tempo (_marker->tempo(), when);
3189 XMLNode &after = map.get_state();
3190 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3191 _editor->commit_reversible_command ();
3194 // delete the dummy marker we used for visual representation while moving.
3195 // a new visual marker will show up automatically.
3200 TempoMarkerDrag::aborted (bool moved)
3202 _marker->set_position (_marker->tempo().frame());
3204 TempoMap& map (_editor->session()->tempo_map());
3205 /* we removed it before, so add it back now */
3206 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3207 // delete the dummy marker we used for visual representation while moving.
3208 // a new visual marker will show up automatically.
3213 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3214 : Drag (e, &c.track_canvas_item(), false)
3218 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3221 /** Do all the things we do when dragging the playhead to make it look as though
3222 * we have located, without actually doing the locate (because that would cause
3223 * the diskstream buffers to be refilled, which is too slow).
3226 CursorDrag::fake_locate (framepos_t t)
3228 _editor->playhead_cursor->set_position (t);
3230 Session* s = _editor->session ();
3231 if (s->timecode_transmission_suspended ()) {
3232 framepos_t const f = _editor->playhead_cursor->current_frame ();
3233 /* This is asynchronous so it will be sent "now"
3235 s->send_mmc_locate (f);
3236 /* These are synchronous and will be sent during the next
3239 s->queue_full_time_code ();
3240 s->queue_song_position_pointer ();
3243 show_verbose_cursor_time (t);
3244 _editor->UpdateAllTransportClocks (t);
3248 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3250 Drag::start_grab (event, c);
3252 _grab_zoom = _editor->samples_per_pixel;
3254 framepos_t where = _editor->canvas_event_sample (event);
3256 _editor->snap_to_with_modifier (where, event);
3258 _editor->_dragging_playhead = true;
3260 Session* s = _editor->session ();
3262 /* grab the track canvas item as well */
3264 _cursor.track_canvas_item().grab();
3267 if (_was_rolling && _stop) {
3271 if (s->is_auditioning()) {
3272 s->cancel_audition ();
3276 if (AudioEngine::instance()->connected()) {
3278 /* do this only if we're the engine is connected
3279 * because otherwise this request will never be
3280 * serviced and we'll busy wait forever. likewise,
3281 * notice if we are disconnected while waiting for the
3282 * request to be serviced.
3285 s->request_suspend_timecode_transmission ();
3286 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3287 /* twiddle our thumbs */
3292 fake_locate (where);
3296 CursorDrag::motion (GdkEvent* event, bool)
3298 framepos_t const adjusted_frame = adjusted_current_frame (event);
3299 if (adjusted_frame != last_pointer_frame()) {
3300 fake_locate (adjusted_frame);
3305 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3307 _editor->_dragging_playhead = false;
3309 _cursor.track_canvas_item().ungrab();
3311 if (!movement_occurred && _stop) {
3315 motion (event, false);
3317 Session* s = _editor->session ();
3319 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3320 _editor->_pending_locate_request = true;
3321 s->request_resume_timecode_transmission ();
3326 CursorDrag::aborted (bool)
3328 _cursor.track_canvas_item().ungrab();
3330 if (_editor->_dragging_playhead) {
3331 _editor->session()->request_resume_timecode_transmission ();
3332 _editor->_dragging_playhead = false;
3335 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3338 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3339 : RegionDrag (e, i, p, v)
3341 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3345 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3347 Drag::start_grab (event, cursor);
3349 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3350 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3352 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3356 FadeInDrag::setup_pointer_frame_offset ()
3358 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3359 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3360 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3364 FadeInDrag::motion (GdkEvent* event, bool)
3366 framecnt_t fade_length;
3367 framepos_t const pos = adjusted_current_frame (event);
3368 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3370 if (pos < (region->position() + 64)) {
3371 fade_length = 64; // this should be a minimum defined somewhere
3372 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3373 fade_length = region->length() - region->fade_out()->back()->when - 1;
3375 fade_length = pos - region->position();
3378 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3380 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3386 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3389 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3393 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3395 if (!movement_occurred) {
3399 framecnt_t fade_length;
3401 framepos_t const pos = adjusted_current_frame (event);
3403 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3405 if (pos < (region->position() + 64)) {
3406 fade_length = 64; // this should be a minimum defined somewhere
3407 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3408 fade_length = region->length() - region->fade_out()->back()->when - 1;
3410 fade_length = pos - region->position();
3413 _editor->begin_reversible_command (_("change fade in length"));
3415 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3417 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3423 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3424 XMLNode &before = alist->get_state();
3426 tmp->audio_region()->set_fade_in_length (fade_length);
3427 tmp->audio_region()->set_fade_in_active (true);
3429 XMLNode &after = alist->get_state();
3430 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3433 _editor->commit_reversible_command ();
3437 FadeInDrag::aborted (bool)
3439 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3440 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3446 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3450 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3451 : RegionDrag (e, i, p, v)
3453 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3457 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3459 Drag::start_grab (event, cursor);
3461 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3462 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3464 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3468 FadeOutDrag::setup_pointer_frame_offset ()
3470 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3471 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3472 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3476 FadeOutDrag::motion (GdkEvent* event, bool)
3478 framecnt_t fade_length;
3480 framepos_t const pos = adjusted_current_frame (event);
3482 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3484 if (pos > (region->last_frame() - 64)) {
3485 fade_length = 64; // this should really be a minimum fade defined somewhere
3486 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3487 fade_length = region->length() - region->fade_in()->back()->when - 1;
3489 fade_length = region->last_frame() - pos;
3492 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3494 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3500 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3503 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3507 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3509 if (!movement_occurred) {
3513 framecnt_t fade_length;
3515 framepos_t const pos = adjusted_current_frame (event);
3517 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3519 if (pos > (region->last_frame() - 64)) {
3520 fade_length = 64; // this should really be a minimum fade defined somewhere
3521 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3522 fade_length = region->length() - region->fade_in()->back()->when - 1;
3524 fade_length = region->last_frame() - pos;
3527 _editor->begin_reversible_command (_("change fade out length"));
3529 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3531 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3537 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3538 XMLNode &before = alist->get_state();
3540 tmp->audio_region()->set_fade_out_length (fade_length);
3541 tmp->audio_region()->set_fade_out_active (true);
3543 XMLNode &after = alist->get_state();
3544 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3547 _editor->commit_reversible_command ();
3551 FadeOutDrag::aborted (bool)
3553 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3554 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3560 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3564 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3567 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3569 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3572 _points.push_back (ArdourCanvas::Duple (0, 0));
3573 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3576 MarkerDrag::~MarkerDrag ()
3578 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3583 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3585 location = new Location (*l);
3586 markers.push_back (m);
3591 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3593 Drag::start_grab (event, cursor);
3597 Location *location = _editor->find_location_from_marker (_marker, is_start);
3598 _editor->_dragging_edit_point = true;
3600 update_item (location);
3602 // _drag_line->show();
3603 // _line->raise_to_top();
3606 show_verbose_cursor_time (location->start());
3608 show_verbose_cursor_time (location->end());
3611 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3614 case Selection::Toggle:
3615 /* we toggle on the button release */
3617 case Selection::Set:
3618 if (!_editor->selection->selected (_marker)) {
3619 _editor->selection->set (_marker);
3622 case Selection::Extend:
3624 Locations::LocationList ll;
3625 list<Marker*> to_add;
3627 _editor->selection->markers.range (s, e);
3628 s = min (_marker->position(), s);
3629 e = max (_marker->position(), e);
3632 if (e < max_framepos) {
3635 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3636 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3637 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3640 to_add.push_back (lm->start);
3643 to_add.push_back (lm->end);
3647 if (!to_add.empty()) {
3648 _editor->selection->add (to_add);
3652 case Selection::Add:
3653 _editor->selection->add (_marker);
3657 /* Set up copies for us to manipulate during the drag
3660 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3662 Location* l = _editor->find_location_from_marker (*i, is_start);
3669 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3671 /* range: check that the other end of the range isn't
3674 CopiedLocationInfo::iterator x;
3675 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3676 if (*(*x).location == *l) {
3680 if (x == _copied_locations.end()) {
3681 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3683 (*x).markers.push_back (*i);
3684 (*x).move_both = true;
3692 MarkerDrag::setup_pointer_frame_offset ()
3695 Location *location = _editor->find_location_from_marker (_marker, is_start);
3696 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3700 MarkerDrag::motion (GdkEvent* event, bool)
3702 framecnt_t f_delta = 0;
3704 bool move_both = false;
3705 Location *real_location;
3706 Location *copy_location = 0;
3708 framepos_t const newframe = adjusted_current_frame (event);
3709 framepos_t next = newframe;
3711 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3715 CopiedLocationInfo::iterator x;
3717 /* find the marker we're dragging, and compute the delta */
3719 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3721 copy_location = (*x).location;
3723 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3725 /* this marker is represented by this
3726 * CopiedLocationMarkerInfo
3729 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3734 if (real_location->is_mark()) {
3735 f_delta = newframe - copy_location->start();
3739 switch (_marker->type()) {
3740 case Marker::SessionStart:
3741 case Marker::RangeStart:
3742 case Marker::LoopStart:
3743 case Marker::PunchIn:
3744 f_delta = newframe - copy_location->start();
3747 case Marker::SessionEnd:
3748 case Marker::RangeEnd:
3749 case Marker::LoopEnd:
3750 case Marker::PunchOut:
3751 f_delta = newframe - copy_location->end();
3754 /* what kind of marker is this ? */
3763 if (x == _copied_locations.end()) {
3764 /* hmm, impossible - we didn't find the dragged marker */
3768 /* now move them all */
3770 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3772 copy_location = x->location;
3774 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3778 if (real_location->locked()) {
3782 if (copy_location->is_mark()) {
3786 copy_location->set_start (copy_location->start() + f_delta);
3790 framepos_t new_start = copy_location->start() + f_delta;
3791 framepos_t new_end = copy_location->end() + f_delta;
3793 if (is_start) { // start-of-range marker
3795 if (move_both || (*x).move_both) {
3796 copy_location->set_start (new_start);
3797 copy_location->set_end (new_end);
3798 } else if (new_start < copy_location->end()) {
3799 copy_location->set_start (new_start);
3800 } else if (newframe > 0) {
3801 _editor->snap_to (next, RoundUpAlways, true);
3802 copy_location->set_end (next);
3803 copy_location->set_start (newframe);
3806 } else { // end marker
3808 if (move_both || (*x).move_both) {
3809 copy_location->set_end (new_end);
3810 copy_location->set_start (new_start);
3811 } else if (new_end > copy_location->start()) {
3812 copy_location->set_end (new_end);
3813 } else if (newframe > 0) {
3814 _editor->snap_to (next, RoundDownAlways, true);
3815 copy_location->set_start (next);
3816 copy_location->set_end (newframe);
3821 update_item (copy_location);
3823 /* now lookup the actual GUI items used to display this
3824 * location and move them to wherever the copy of the location
3825 * is now. This means that the logic in ARDOUR::Location is
3826 * still enforced, even though we are not (yet) modifying
3827 * the real Location itself.
3830 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3833 lm->set_position (copy_location->start(), copy_location->end());
3838 assert (!_copied_locations.empty());
3840 show_verbose_cursor_time (newframe);
3844 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3846 if (!movement_occurred) {
3848 if (was_double_click()) {
3849 _editor->rename_marker (_marker);
3853 /* just a click, do nothing but finish
3854 off the selection process
3857 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3860 case Selection::Set:
3861 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3862 _editor->selection->set (_marker);
3866 case Selection::Toggle:
3867 /* we toggle on the button release, click only */
3868 _editor->selection->toggle (_marker);
3871 case Selection::Extend:
3872 case Selection::Add:
3879 _editor->_dragging_edit_point = false;
3881 _editor->begin_reversible_command ( _("move marker") );
3882 XMLNode &before = _editor->session()->locations()->get_state();
3884 MarkerSelection::iterator i;
3885 CopiedLocationInfo::iterator x;
3888 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3889 x != _copied_locations.end() && i != _editor->selection->markers.end();
3892 Location * location = _editor->find_location_from_marker (*i, is_start);
3896 if (location->locked()) {
3900 if (location->is_mark()) {
3901 location->set_start (((*x).location)->start());
3903 location->set (((*x).location)->start(), ((*x).location)->end());
3908 XMLNode &after = _editor->session()->locations()->get_state();
3909 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3910 _editor->commit_reversible_command ();
3914 MarkerDrag::aborted (bool movement_occured)
3916 if (!movement_occured) {
3920 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3922 /* move all markers to their original location */
3925 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3928 Location * location = _editor->find_location_from_marker (*m, is_start);
3931 (*m)->set_position (is_start ? location->start() : location->end());
3938 MarkerDrag::update_item (Location*)
3943 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3945 _cumulative_x_drag (0),
3946 _cumulative_y_drag (0)
3948 if (_zero_gain_fraction < 0.0) {
3949 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3952 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3954 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3960 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3962 Drag::start_grab (event, _editor->cursors()->fader);
3964 // start the grab at the center of the control point so
3965 // the point doesn't 'jump' to the mouse after the first drag
3966 _fixed_grab_x = _point->get_x();
3967 _fixed_grab_y = _point->get_y();
3969 float const fraction = 1 - (_point->get_y() / _point->line().height());
3971 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3973 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3975 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3977 if (!_point->can_slide ()) {
3978 _x_constrained = true;
3983 ControlPointDrag::motion (GdkEvent* event, bool)
3985 double dx = _drags->current_pointer_x() - last_pointer_x();
3986 double dy = current_pointer_y() - last_pointer_y();
3988 if (event->button.state & Keyboard::SecondaryModifier) {
3993 /* coordinate in pixels relative to the start of the region (for region-based automation)
3994 or track (for track-based automation) */
3995 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3996 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3998 // calculate zero crossing point. back off by .01 to stay on the
3999 // positive side of zero
4000 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4002 // make sure we hit zero when passing through
4003 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4007 if (_x_constrained) {
4010 if (_y_constrained) {
4014 _cumulative_x_drag = cx - _fixed_grab_x;
4015 _cumulative_y_drag = cy - _fixed_grab_y;
4019 cy = min ((double) _point->line().height(), cy);
4021 framepos_t cx_frames = _editor->pixel_to_sample (cx);
4023 if (!_x_constrained) {
4024 _editor->snap_to_with_modifier (cx_frames, event);
4027 cx_frames = min (cx_frames, _point->line().maximum_time());
4029 float const fraction = 1.0 - (cy / _point->line().height());
4031 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4033 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4037 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4039 if (!movement_occurred) {
4043 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4044 _editor->reset_point_selection ();
4048 motion (event, false);
4051 _point->line().end_drag (_pushing, _final_index);
4052 _editor->commit_reversible_command ();
4056 ControlPointDrag::aborted (bool)
4058 _point->line().reset ();
4062 ControlPointDrag::active (Editing::MouseMode m)
4064 if (m == Editing::MouseDraw) {
4065 /* always active in mouse draw */
4069 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4070 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4073 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4076 _cumulative_y_drag (0)
4078 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4082 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4084 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4087 _item = &_line->grab_item ();
4089 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4090 origin, and ditto for y.
4093 double cx = event->button.x;
4094 double cy = event->button.y;
4096 _line->parent_group().canvas_to_item (cx, cy);
4098 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4103 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
4104 /* no adjacent points */
4108 Drag::start_grab (event, _editor->cursors()->fader);
4110 /* store grab start in parent frame */
4115 double fraction = 1.0 - (cy / _line->height());
4117 _line->start_drag_line (before, after, fraction);
4119 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4123 LineDrag::motion (GdkEvent* event, bool)
4125 double dy = current_pointer_y() - last_pointer_y();
4127 if (event->button.state & Keyboard::SecondaryModifier) {
4131 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4133 _cumulative_y_drag = cy - _fixed_grab_y;
4136 cy = min ((double) _line->height(), cy);
4138 double const fraction = 1.0 - (cy / _line->height());
4141 /* we are ignoring x position for this drag, so we can just pass in anything */
4142 _line->drag_motion (0, fraction, true, false, ignored);
4144 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4148 LineDrag::finished (GdkEvent* event, bool movement_occured)
4150 if (movement_occured) {
4151 motion (event, false);
4152 _line->end_drag (false, 0);
4154 /* add a new control point on the line */
4156 AutomationTimeAxisView* atv;
4158 _line->end_drag (false, 0);
4160 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4161 framepos_t where = _editor->window_event_sample (event, 0, 0);
4162 atv->add_automation_event (event, where, event->button.y, false);
4166 _editor->commit_reversible_command ();
4170 LineDrag::aborted (bool)
4175 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4178 _cumulative_x_drag (0)
4180 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4184 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4186 Drag::start_grab (event);
4188 _line = reinterpret_cast<Line*> (_item);
4191 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4193 double cx = event->button.x;
4194 double cy = event->button.y;
4196 _item->parent()->canvas_to_item (cx, cy);
4198 /* store grab start in parent frame */
4199 _region_view_grab_x = cx;
4201 _before = *(float*) _item->get_data ("position");
4203 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4205 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4209 FeatureLineDrag::motion (GdkEvent*, bool)
4211 double dx = _drags->current_pointer_x() - last_pointer_x();
4213 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4215 _cumulative_x_drag += dx;
4217 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4226 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4228 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4230 float *pos = new float;
4233 _line->set_data ("position", pos);
4239 FeatureLineDrag::finished (GdkEvent*, bool)
4241 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4242 _arv->update_transient(_before, _before);
4246 FeatureLineDrag::aborted (bool)
4251 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4253 , _vertical_only (false)
4255 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4259 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4261 Drag::start_grab (event);
4262 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4266 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4273 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4275 framepos_t grab = grab_frame ();
4276 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4277 _editor->snap_to_with_modifier (grab, event);
4279 grab = raw_grab_frame ();
4282 /* base start and end on initial click position */
4292 if (current_pointer_y() < grab_y()) {
4293 y1 = current_pointer_y();
4296 y2 = current_pointer_y();
4300 if (start != end || y1 != y2) {
4302 double x1 = _editor->sample_to_pixel (start);
4303 double x2 = _editor->sample_to_pixel (end);
4304 const double min_dimension = 2.0;
4306 if (_vertical_only) {
4307 /* fixed 10 pixel width */
4311 x2 = min (x1 - min_dimension, x2);
4313 x2 = max (x1 + min_dimension, x2);
4318 y2 = min (y1 - min_dimension, y2);
4320 y2 = max (y1 + min_dimension, y2);
4323 /* translate rect into item space and set */
4325 ArdourCanvas::Rect r (x1, y1, x2, y2);
4327 /* this drag is a _trackview_only == true drag, so the y1 and
4328 * y2 (computed using current_pointer_y() and grab_y()) will be
4329 * relative to the top of the trackview group). The
4330 * rubberband rect has the same parent/scroll offset as the
4331 * the trackview group, so we can use the "r" rect directly
4332 * to set the shape of the rubberband.
4335 _editor->rubberband_rect->set (r);
4336 _editor->rubberband_rect->show();
4337 _editor->rubberband_rect->raise_to_top();
4339 show_verbose_cursor_time (pf);
4341 do_select_things (event, true);
4346 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4350 framepos_t grab = grab_frame ();
4351 framepos_t lpf = last_pointer_frame ();
4353 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4354 grab = raw_grab_frame ();
4355 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4369 if (current_pointer_y() < grab_y()) {
4370 y1 = current_pointer_y();
4373 y2 = current_pointer_y();
4377 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4381 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4383 if (movement_occurred) {
4385 motion (event, false);
4386 do_select_things (event, false);
4392 bool do_deselect = true;
4393 MidiTimeAxisView* mtv;
4395 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4397 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4398 /* nothing selected */
4399 add_midi_region (mtv);
4400 do_deselect = false;
4404 /* do not deselect if Primary or Tertiary (toggle-select or
4405 * extend-select are pressed.
4408 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4409 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4416 _editor->rubberband_rect->hide();
4420 RubberbandSelectDrag::aborted (bool)
4422 _editor->rubberband_rect->hide ();
4425 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4426 : RegionDrag (e, i, p, v)
4428 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4432 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4434 Drag::start_grab (event, cursor);
4436 show_verbose_cursor_time (adjusted_current_frame (event));
4440 TimeFXDrag::motion (GdkEvent* event, bool)
4442 RegionView* rv = _primary;
4443 StreamView* cv = rv->get_time_axis_view().view ();
4445 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4446 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4447 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4449 framepos_t const pf = adjusted_current_frame (event);
4451 if (pf > rv->region()->position()) {
4452 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4455 show_verbose_cursor_time (pf);
4459 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4461 _primary->get_time_axis_view().hide_timestretch ();
4463 if (!movement_occurred) {
4467 if (last_pointer_frame() < _primary->region()->position()) {
4468 /* backwards drag of the left edge - not usable */
4472 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4474 float percentage = (double) newlen / (double) _primary->region()->length();
4476 #ifndef USE_RUBBERBAND
4477 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4478 if (_primary->region()->data_type() == DataType::AUDIO) {
4479 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4483 if (!_editor->get_selection().regions.empty()) {
4484 /* primary will already be included in the selection, and edit
4485 group shared editing will propagate selection across
4486 equivalent regions, so just use the current region
4490 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4491 error << _("An error occurred while executing time stretch operation") << endmsg;
4497 TimeFXDrag::aborted (bool)
4499 _primary->get_time_axis_view().hide_timestretch ();
4502 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4505 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4509 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4511 Drag::start_grab (event);
4515 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4517 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4521 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4523 if (movement_occurred && _editor->session()) {
4524 /* make sure we stop */
4525 _editor->session()->request_transport_speed (0.0);
4530 ScrubDrag::aborted (bool)
4535 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4539 , _time_selection_at_start (!_editor->get_selection().time.empty())
4541 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4543 if (_time_selection_at_start) {
4544 start_at_start = _editor->get_selection().time.start();
4545 end_at_start = _editor->get_selection().time.end_frame();
4550 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4552 if (_editor->session() == 0) {
4556 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4558 switch (_operation) {
4559 case CreateSelection:
4560 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4565 cursor = _editor->cursors()->selector;
4566 Drag::start_grab (event, cursor);
4569 case SelectionStartTrim:
4570 if (_editor->clicked_axisview) {
4571 _editor->clicked_axisview->order_selection_trims (_item, true);
4573 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4576 case SelectionEndTrim:
4577 if (_editor->clicked_axisview) {
4578 _editor->clicked_axisview->order_selection_trims (_item, false);
4580 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4584 Drag::start_grab (event, cursor);
4587 case SelectionExtend:
4588 Drag::start_grab (event, cursor);
4592 if (_operation == SelectionMove) {
4593 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4595 show_verbose_cursor_time (adjusted_current_frame (event));
4600 SelectionDrag::setup_pointer_frame_offset ()
4602 switch (_operation) {
4603 case CreateSelection:
4604 _pointer_frame_offset = 0;
4607 case SelectionStartTrim:
4609 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4612 case SelectionEndTrim:
4613 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4616 case SelectionExtend:
4622 SelectionDrag::motion (GdkEvent* event, bool first_move)
4624 framepos_t start = 0;
4626 framecnt_t length = 0;
4627 framecnt_t distance = 0;
4629 framepos_t const pending_position = adjusted_current_frame (event);
4631 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4635 switch (_operation) {
4636 case CreateSelection:
4638 framepos_t grab = grab_frame ();
4641 grab = adjusted_current_frame (event, false);
4642 if (grab < pending_position) {
4643 _editor->snap_to (grab, RoundDownMaybe);
4645 _editor->snap_to (grab, RoundUpMaybe);
4649 if (pending_position < grab) {
4650 start = pending_position;
4653 end = pending_position;
4657 /* first drag: Either add to the selection
4658 or create a new selection
4665 /* adding to the selection */
4666 _editor->set_selected_track_as_side_effect (Selection::Add);
4667 _editor->clicked_selection = _editor->selection->add (start, end);
4674 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4675 _editor->set_selected_track_as_side_effect (Selection::Set);
4678 _editor->clicked_selection = _editor->selection->set (start, end);
4682 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4683 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4684 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4686 _editor->selection->add (atest);
4690 /* select all tracks within the rectangle that we've marked out so far */
4691 TrackViewList new_selection;
4692 TrackViewList& all_tracks (_editor->track_views);
4694 ArdourCanvas::Coord const top = grab_y();
4695 ArdourCanvas::Coord const bottom = current_pointer_y();
4697 if (top >= 0 && bottom >= 0) {
4699 //first, find the tracks that are covered in the y range selection
4700 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4701 if ((*i)->covered_by_y_range (top, bottom)) {
4702 new_selection.push_back (*i);
4706 //now find any tracks that are GROUPED with the tracks we selected
4707 TrackViewList grouped_add = new_selection;
4708 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4709 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4710 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4711 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4712 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4713 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4714 grouped_add.push_back (*j);
4719 //now compare our list with the current selection, and add or remove as necessary
4720 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4721 TrackViewList tracks_to_add;
4722 TrackViewList tracks_to_remove;
4723 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4724 if ( !_editor->selection->tracks.contains ( *i ) )
4725 tracks_to_add.push_back ( *i );
4726 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4727 if ( !grouped_add.contains ( *i ) )
4728 tracks_to_remove.push_back ( *i );
4729 _editor->selection->add(tracks_to_add);
4730 _editor->selection->remove(tracks_to_remove);
4736 case SelectionStartTrim:
4738 start = _editor->selection->time[_editor->clicked_selection].start;
4739 end = _editor->selection->time[_editor->clicked_selection].end;
4741 if (pending_position > end) {
4744 start = pending_position;
4748 case SelectionEndTrim:
4750 start = _editor->selection->time[_editor->clicked_selection].start;
4751 end = _editor->selection->time[_editor->clicked_selection].end;
4753 if (pending_position < start) {
4756 end = pending_position;
4763 start = _editor->selection->time[_editor->clicked_selection].start;
4764 end = _editor->selection->time[_editor->clicked_selection].end;
4766 length = end - start;
4767 distance = pending_position - start;
4768 start = pending_position;
4769 _editor->snap_to (start);
4771 end = start + length;
4775 case SelectionExtend:
4780 switch (_operation) {
4782 if (_time_selection_at_start) {
4783 _editor->selection->move_time (distance);
4787 _editor->selection->replace (_editor->clicked_selection, start, end);
4791 if (_operation == SelectionMove) {
4792 show_verbose_cursor_time(start);
4794 show_verbose_cursor_time(pending_position);
4799 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4801 Session* s = _editor->session();
4803 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4804 if (movement_occurred) {
4805 motion (event, false);
4806 /* XXX this is not object-oriented programming at all. ick */
4807 if (_editor->selection->time.consolidate()) {
4808 _editor->selection->TimeChanged ();
4811 /* XXX what if its a music time selection? */
4813 if ( s->get_play_range() && s->transport_rolling() ) {
4814 s->request_play_range (&_editor->selection->time, true);
4816 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4817 if (_operation == SelectionEndTrim)
4818 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4820 s->request_locate (_editor->get_selection().time.start());
4826 /* just a click, no pointer movement.
4829 if (_operation == SelectionExtend) {
4830 if (_time_selection_at_start) {
4831 framepos_t pos = adjusted_current_frame (event, false);
4832 framepos_t start = min (pos, start_at_start);
4833 framepos_t end = max (pos, end_at_start);
4834 _editor->selection->set (start, end);
4837 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4838 if (_editor->clicked_selection) {
4839 _editor->selection->remove (_editor->clicked_selection);
4842 if (!_editor->clicked_selection) {
4843 _editor->selection->clear_time();
4848 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4849 _editor->selection->set (_editor->clicked_axisview);
4852 if (s && s->get_play_range () && s->transport_rolling()) {
4853 s->request_stop (false, false);
4858 _editor->stop_canvas_autoscroll ();
4859 _editor->clicked_selection = 0;
4860 _editor->commit_reversible_selection_op ();
4864 SelectionDrag::aborted (bool)
4869 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4870 : Drag (e, i, false),
4874 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4876 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4877 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4878 physical_screen_height (_editor->get_window())));
4879 _drag_rect->hide ();
4881 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4882 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4885 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4887 /* normal canvas items will be cleaned up when their parent group is deleted. But
4888 this item is created as the child of a long-lived parent group, and so we
4889 need to explicitly delete it.
4895 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4897 if (_editor->session() == 0) {
4901 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4903 if (!_editor->temp_location) {
4904 _editor->temp_location = new Location (*_editor->session());
4907 switch (_operation) {
4908 case CreateSkipMarker:
4909 case CreateRangeMarker:
4910 case CreateTransportMarker:
4911 case CreateCDMarker:
4913 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4918 cursor = _editor->cursors()->selector;
4922 Drag::start_grab (event, cursor);
4924 show_verbose_cursor_time (adjusted_current_frame (event));
4928 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4930 framepos_t start = 0;
4932 ArdourCanvas::Rectangle *crect;
4934 switch (_operation) {
4935 case CreateSkipMarker:
4936 crect = _editor->range_bar_drag_rect;
4938 case CreateRangeMarker:
4939 crect = _editor->range_bar_drag_rect;
4941 case CreateTransportMarker:
4942 crect = _editor->transport_bar_drag_rect;
4944 case CreateCDMarker:
4945 crect = _editor->cd_marker_bar_drag_rect;
4948 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4953 framepos_t const pf = adjusted_current_frame (event);
4955 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4956 framepos_t grab = grab_frame ();
4957 _editor->snap_to (grab);
4959 if (pf < grab_frame()) {
4967 /* first drag: Either add to the selection
4968 or create a new selection.
4973 _editor->temp_location->set (start, end);
4977 update_item (_editor->temp_location);
4979 //_drag_rect->raise_to_top();
4985 _editor->temp_location->set (start, end);
4987 double x1 = _editor->sample_to_pixel (start);
4988 double x2 = _editor->sample_to_pixel (end);
4992 update_item (_editor->temp_location);
4995 show_verbose_cursor_time (pf);
5000 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5002 Location * newloc = 0;
5006 if (movement_occurred) {
5007 motion (event, false);
5010 switch (_operation) {
5011 case CreateSkipMarker:
5012 case CreateRangeMarker:
5013 case CreateCDMarker:
5015 XMLNode &before = _editor->session()->locations()->get_state();
5016 if (_operation == CreateSkipMarker) {
5017 _editor->begin_reversible_command (_("new skip marker"));
5018 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5019 flags = Location::IsRangeMarker | Location::IsSkip;
5020 _editor->range_bar_drag_rect->hide();
5021 } else if (_operation == CreateCDMarker) {
5022 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5023 _editor->begin_reversible_command (_("new CD marker"));
5024 flags = Location::IsRangeMarker | Location::IsCDMarker;
5025 _editor->cd_marker_bar_drag_rect->hide();
5027 _editor->begin_reversible_command (_("new skip marker"));
5028 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5029 flags = Location::IsRangeMarker;
5030 _editor->range_bar_drag_rect->hide();
5032 newloc = new Location (
5033 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5036 _editor->session()->locations()->add (newloc, true);
5037 XMLNode &after = _editor->session()->locations()->get_state();
5038 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5039 _editor->commit_reversible_command ();
5043 case CreateTransportMarker:
5044 // popup menu to pick loop or punch
5045 _editor->new_transport_marker_context_menu (&event->button, _item);
5051 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5053 if (_operation == CreateTransportMarker) {
5055 /* didn't drag, so just locate */
5057 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5059 } else if (_operation == CreateCDMarker) {
5061 /* didn't drag, but mark is already created so do
5064 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5069 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5071 if (end == max_framepos) {
5072 end = _editor->session()->current_end_frame ();
5075 if (start == max_framepos) {
5076 start = _editor->session()->current_start_frame ();
5079 switch (_editor->mouse_mode) {
5081 /* find the two markers on either side and then make the selection from it */
5082 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5086 /* find the two markers on either side of the click and make the range out of it */
5087 _editor->selection->set (start, end);
5096 _editor->stop_canvas_autoscroll ();
5100 RangeMarkerBarDrag::aborted (bool movement_occured)
5102 if (movement_occured) {
5103 _drag_rect->hide ();
5108 RangeMarkerBarDrag::update_item (Location* location)
5110 double const x1 = _editor->sample_to_pixel (location->start());
5111 double const x2 = _editor->sample_to_pixel (location->end());
5113 _drag_rect->set_x0 (x1);
5114 _drag_rect->set_x1 (x2);
5117 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5119 , _cumulative_dx (0)
5120 , _cumulative_dy (0)
5122 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5124 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5126 _region = &_primary->region_view ();
5127 _note_height = _region->midi_stream_view()->note_height ();
5131 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5133 Drag::start_grab (event);
5135 if (!(_was_selected = _primary->selected())) {
5137 /* tertiary-click means extend selection - we'll do that on button release,
5138 so don't add it here, because otherwise we make it hard to figure
5139 out the "extend-to" range.
5142 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5145 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5148 _region->note_selected (_primary, true);
5150 _region->unique_select (_primary);
5153 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5154 _editor->commit_reversible_selection_op();
5159 /** @return Current total drag x change in frames */
5161 NoteDrag::total_dx () const
5164 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5166 /* primary note time */
5167 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5169 /* new time of the primary note in session frames */
5170 frameoffset_t st = n + dx;
5172 framepos_t const rp = _region->region()->position ();
5174 /* prevent the note being dragged earlier than the region's position */
5177 /* snap and return corresponding delta */
5178 return _region->snap_frame_to_frame (st - rp) + rp - n;
5181 /** @return Current total drag y change in note number */
5183 NoteDrag::total_dy () const
5185 MidiStreamView* msv = _region->midi_stream_view ();
5186 double const y = _region->midi_view()->y_position ();
5187 /* new current note */
5188 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5190 n = max (msv->lowest_note(), n);
5191 n = min (msv->highest_note(), n);
5192 /* and work out delta */
5193 return n - msv->y_to_note (grab_y() - y);
5197 NoteDrag::motion (GdkEvent *, bool)
5199 /* Total change in x and y since the start of the drag */
5200 frameoffset_t const dx = total_dx ();
5201 int8_t const dy = total_dy ();
5203 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5204 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5205 double const tdy = -dy * _note_height - _cumulative_dy;
5208 _cumulative_dx += tdx;
5209 _cumulative_dy += tdy;
5211 int8_t note_delta = total_dy();
5213 _region->move_selection (tdx, tdy, note_delta);
5215 /* the new note value may be the same as the old one, but we
5216 * don't know what that means because the selection may have
5217 * involved more than one note and we might be doing something
5218 * odd with them. so show the note value anyway, always.
5222 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5224 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5225 (int) floor ((double)new_note));
5227 show_verbose_cursor_text (buf);
5232 NoteDrag::finished (GdkEvent* ev, bool moved)
5235 /* no motion - select note */
5237 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5238 _editor->current_mouse_mode() == Editing::MouseDraw) {
5240 bool changed = false;
5242 if (_was_selected) {
5243 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5245 _region->note_deselected (_primary);
5249 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5250 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5252 if (!extend && !add && _region->selection_size() > 1) {
5253 _region->unique_select (_primary);
5255 } else if (extend) {
5256 _region->note_selected (_primary, true, true);
5259 /* it was added during button press */
5264 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5265 _editor->commit_reversible_selection_op();
5269 _region->note_dropped (_primary, total_dx(), total_dy());
5274 NoteDrag::aborted (bool)
5279 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5280 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5281 : Drag (editor, atv->base_item ())
5283 , _y_origin (atv->y_position())
5284 , _nothing_to_drag (false)
5286 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5287 setup (atv->lines ());
5290 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5291 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5292 : Drag (editor, rv->get_canvas_group ())
5294 , _y_origin (rv->get_time_axis_view().y_position())
5295 , _nothing_to_drag (false)
5298 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5300 list<boost::shared_ptr<AutomationLine> > lines;
5302 AudioRegionView* audio_view;
5303 AutomationRegionView* automation_view;
5304 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5305 lines.push_back (audio_view->get_gain_line ());
5306 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5307 lines.push_back (automation_view->line ());
5310 error << _("Automation range drag created for invalid region type") << endmsg;
5316 /** @param lines AutomationLines to drag.
5317 * @param offset Offset from the session start to the points in the AutomationLines.
5320 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5322 /* find the lines that overlap the ranges being dragged */
5323 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5324 while (i != lines.end ()) {
5325 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5328 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5330 /* check this range against all the AudioRanges that we are using */
5331 list<AudioRange>::const_iterator k = _ranges.begin ();
5332 while (k != _ranges.end()) {
5333 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5339 /* add it to our list if it overlaps at all */
5340 if (k != _ranges.end()) {
5345 _lines.push_back (n);
5351 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5355 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5357 return 1.0 - ((global_y - _y_origin) / line->height());
5361 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5363 const double v = list->eval(x);
5364 return _integral ? rint(v) : v;
5368 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5370 Drag::start_grab (event, cursor);
5372 /* Get line states before we start changing things */
5373 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5374 i->state = &i->line->get_state ();
5375 i->original_fraction = y_fraction (i->line, current_pointer_y());
5378 if (_ranges.empty()) {
5380 /* No selected time ranges: drag all points */
5381 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5382 uint32_t const N = i->line->npoints ();
5383 for (uint32_t j = 0; j < N; ++j) {
5384 i->points.push_back (i->line->nth (j));
5390 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5392 framecnt_t const half = (i->start + i->end) / 2;
5394 /* find the line that this audio range starts in */
5395 list<Line>::iterator j = _lines.begin();
5396 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5400 if (j != _lines.end()) {
5401 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5403 /* j is the line that this audio range starts in; fade into it;
5404 64 samples length plucked out of thin air.
5407 framepos_t a = i->start + 64;
5412 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5413 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5415 the_list->editor_add (p, value (the_list, p));
5416 the_list->editor_add (q, value (the_list, q));
5419 /* same thing for the end */
5422 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5426 if (j != _lines.end()) {
5427 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5429 /* j is the line that this audio range starts in; fade out of it;
5430 64 samples length plucked out of thin air.
5433 framepos_t b = i->end - 64;
5438 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5439 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5441 the_list->editor_add (p, value (the_list, p));
5442 the_list->editor_add (q, value (the_list, q));
5446 _nothing_to_drag = true;
5448 /* Find all the points that should be dragged and put them in the relevant
5449 points lists in the Line structs.
5452 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5454 uint32_t const N = i->line->npoints ();
5455 for (uint32_t j = 0; j < N; ++j) {
5457 /* here's a control point on this line */
5458 ControlPoint* p = i->line->nth (j);
5459 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5461 /* see if it's inside a range */
5462 list<AudioRange>::const_iterator k = _ranges.begin ();
5463 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5467 if (k != _ranges.end()) {
5468 /* dragging this point */
5469 _nothing_to_drag = false;
5470 i->points.push_back (p);
5476 if (_nothing_to_drag) {
5480 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5481 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5486 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5488 if (_nothing_to_drag) {
5492 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5493 float const f = y_fraction (l->line, current_pointer_y());
5494 /* we are ignoring x position for this drag, so we can just pass in anything */
5496 l->line->drag_motion (0, f, true, false, ignored);
5497 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5502 AutomationRangeDrag::finished (GdkEvent* event, bool)
5504 if (_nothing_to_drag) {
5508 motion (event, false);
5509 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5510 i->line->end_drag (false, 0);
5513 _editor->commit_reversible_command ();
5517 AutomationRangeDrag::aborted (bool)
5519 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5524 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5526 , initial_time_axis_view (itav)
5528 /* note that time_axis_view may be null if the regionview was created
5529 * as part of a copy operation.
5531 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5532 layer = v->region()->layer ();
5533 initial_y = v->get_canvas_group()->position().y;
5534 initial_playlist = v->region()->playlist ();
5535 initial_position = v->region()->position ();
5536 initial_end = v->region()->position () + v->region()->length ();
5539 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5540 : Drag (e, i->canvas_item ())
5543 , _cumulative_dx (0)
5545 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5546 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5551 PatchChangeDrag::motion (GdkEvent* ev, bool)
5553 framepos_t f = adjusted_current_frame (ev);
5554 boost::shared_ptr<Region> r = _region_view->region ();
5555 f = max (f, r->position ());
5556 f = min (f, r->last_frame ());
5558 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5559 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5560 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5561 _cumulative_dx = dxu;
5565 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5567 if (!movement_occurred) {
5571 boost::shared_ptr<Region> r (_region_view->region ());
5572 framepos_t f = adjusted_current_frame (ev);
5573 f = max (f, r->position ());
5574 f = min (f, r->last_frame ());
5576 _region_view->move_patch_change (
5578 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5583 PatchChangeDrag::aborted (bool)
5585 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5589 PatchChangeDrag::setup_pointer_frame_offset ()
5591 boost::shared_ptr<Region> region = _region_view->region ();
5592 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5595 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5596 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5603 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5605 _region_view->update_drag_selection (
5607 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5611 MidiRubberbandSelectDrag::deselect_things ()
5616 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5617 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5620 _vertical_only = true;
5624 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5626 double const y = _region_view->midi_view()->y_position ();
5628 y1 = max (0.0, y1 - y);
5629 y2 = max (0.0, y2 - y);
5631 _region_view->update_vertical_drag_selection (
5634 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5639 MidiVerticalSelectDrag::deselect_things ()
5644 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5645 : RubberbandSelectDrag (e, i)
5651 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5653 if (drag_in_progress) {
5654 /* We just want to select things at the end of the drag, not during it */
5658 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5660 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5662 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5664 _editor->commit_reversible_selection_op ();
5668 EditorRubberbandSelectDrag::deselect_things ()
5670 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5672 _editor->selection->clear_tracks();
5673 _editor->selection->clear_regions();
5674 _editor->selection->clear_points ();
5675 _editor->selection->clear_lines ();
5676 _editor->selection->clear_midi_notes ();
5678 _editor->commit_reversible_selection_op();
5681 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5686 _note[0] = _note[1] = 0;
5689 NoteCreateDrag::~NoteCreateDrag ()
5695 NoteCreateDrag::grid_frames (framepos_t t) const
5698 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5700 grid_beats = Evoral::Beats(1);
5703 return _region_view->region_beats_to_region_frames (grid_beats);
5707 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5709 Drag::start_grab (event, cursor);
5711 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5713 framepos_t pf = _drags->current_pointer_frame ();
5714 framecnt_t const g = grid_frames (pf);
5716 /* Hack so that we always snap to the note that we are over, instead of snapping
5717 to the next one if we're more than halfway through the one we're over.
5719 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5723 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5724 _note[1] = _note[0];
5726 MidiStreamView* sv = _region_view->midi_stream_view ();
5727 double const x = _editor->sample_to_pixel (_note[0]);
5728 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5730 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5731 _drag_rect->set_outline_all ();
5732 _drag_rect->set_outline_color (0xffffff99);
5733 _drag_rect->set_fill_color (0xffffff66);
5737 NoteCreateDrag::motion (GdkEvent* event, bool)
5739 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5740 double const x0 = _editor->sample_to_pixel (_note[0]);
5741 double const x1 = _editor->sample_to_pixel (_note[1]);
5742 _drag_rect->set_x0 (std::min(x0, x1));
5743 _drag_rect->set_x1 (std::max(x0, x1));
5747 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5749 if (!had_movement) {
5753 framepos_t const start = min (_note[0], _note[1]);
5754 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5756 framecnt_t const g = grid_frames (start);
5757 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5759 if (_editor->snap_mode() == SnapNormal && length < g) {
5763 Evoral::Beats length_beats = max (
5764 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5766 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5770 NoteCreateDrag::y_to_region (double y) const
5773 _region_view->get_canvas_group()->canvas_to_item (x, y);
5778 NoteCreateDrag::aborted (bool)
5783 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5788 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5792 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5794 Drag::start_grab (event, cursor);
5798 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5804 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5807 distance = _drags->current_pointer_x() - grab_x();
5808 len = ar->fade_in()->back()->when;
5810 distance = grab_x() - _drags->current_pointer_x();
5811 len = ar->fade_out()->back()->when;
5814 /* how long should it be ? */
5816 new_length = len + _editor->pixel_to_sample (distance);
5818 /* now check with the region that this is legal */
5820 new_length = ar->verify_xfade_bounds (new_length, start);
5823 arv->reset_fade_in_shape_width (ar, new_length);
5825 arv->reset_fade_out_shape_width (ar, new_length);
5830 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5836 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5839 distance = _drags->current_pointer_x() - grab_x();
5840 len = ar->fade_in()->back()->when;
5842 distance = grab_x() - _drags->current_pointer_x();
5843 len = ar->fade_out()->back()->when;
5846 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5848 _editor->begin_reversible_command ("xfade trim");
5849 ar->playlist()->clear_owned_changes ();
5852 ar->set_fade_in_length (new_length);
5854 ar->set_fade_out_length (new_length);
5857 /* Adjusting the xfade may affect other regions in the playlist, so we need
5858 to get undo Commands from the whole playlist rather than just the
5862 vector<Command*> cmds;
5863 ar->playlist()->rdiff (cmds);
5864 _editor->session()->add_commands (cmds);
5865 _editor->commit_reversible_command ();
5870 CrossfadeEdgeDrag::aborted (bool)
5873 // arv->redraw_start_xfade ();
5875 // arv->redraw_end_xfade ();
5879 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5880 : Drag (e, item, true)
5881 , line (new EditorCursor (*e))
5883 line->set_position (pos);
5887 RegionCutDrag::~RegionCutDrag ()
5893 RegionCutDrag::motion (GdkEvent*, bool)
5895 framepos_t where = _drags->current_pointer_frame();
5896 _editor->snap_to (where);
5898 line->set_position (where);
5902 RegionCutDrag::finished (GdkEvent*, bool)
5904 _editor->get_track_canvas()->canvas()->re_enter();
5906 framepos_t pos = _drags->current_pointer_frame();
5910 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5916 _editor->split_regions_at (pos, rs);
5920 RegionCutDrag::aborted (bool)