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 = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1015 int track_index = i->time_axis_view + this_delta_time_axis_view;
1016 assert(track_index >= 0);
1018 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1019 /* Track is in the Dropzone */
1021 i->time_axis_view = track_index;
1022 assert(i->time_axis_view >= (int) _time_axis_views.size());
1025 double yposition = 0;
1026 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1027 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1030 /* store index of each new playlist as a negative count, starting at -1 */
1032 if (pdz == playlist_dropzone_map.end()) {
1033 /* compute where this new track (which doesn't exist yet) will live
1036 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1038 /* How high is this region view ? */
1040 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1041 ArdourCanvas::Rect bbox;
1044 bbox = obbox.get ();
1047 last_track_bottom_edge += bbox.height();
1049 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1052 yposition = pdz->second;
1055 /* values are zero or negative, hence the use of min() */
1056 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1061 /* The TimeAxisView that this region is now over */
1062 TimeAxisView* current_tv = _time_axis_views[track_index];
1064 /* Ensure it is moved from stacked -> expanded if appropriate */
1065 if (current_tv->view()->layer_display() == Stacked) {
1066 current_tv->view()->set_layer_display (Expanded);
1069 /* We're only allowed to go -ve in layer on Expanded views */
1070 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1071 this_delta_layer = - i->layer;
1075 rv->set_height (current_tv->view()->child_height ());
1077 /* Update show/hidden status as the region view may have come from a hidden track,
1078 or have moved to one.
1080 if (current_tv->hidden ()) {
1081 rv->get_canvas_group()->hide ();
1083 rv->get_canvas_group()->show ();
1086 /* Update the DraggingView */
1087 i->time_axis_view = track_index;
1088 i->layer += this_delta_layer;
1091 _editor->mouse_brush_insert_region (rv, pending_region_position);
1095 /* Get the y coordinate of the top of the track that this region is now over */
1096 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1098 /* And adjust for the layer that it should be on */
1099 StreamView* cv = current_tv->view ();
1100 switch (cv->layer_display ()) {
1104 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1107 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1111 /* need to get the parent of the regionview
1112 * canvas group and get its position in
1113 * equivalent coordinate space as the trackview
1114 * we are now dragging over.
1117 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1122 /* Now move the region view */
1123 rv->move (x_delta, y_delta);
1125 } /* foreach region */
1127 _total_x_delta += x_delta;
1129 if (x_delta != 0 && !_brushing) {
1130 show_verbose_cursor_time (_last_frame_position);
1133 /* keep track of pointer movement */
1135 /* the pointer is currently over a time axis view */
1137 if (_last_pointer_time_axis_view < 0) {
1138 /* last motion event was not over a time axis view
1139 * or last y-movement out of the dropzone was not valid
1142 if (delta_time_axis_view < 0) {
1143 /* in the drop zone, moving up */
1145 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1146 * We do not use negative _last_pointer_time_axis_view because
1147 * the dropzone is "packed" (the actual track offset is ignored)
1149 * As opposed to the actual number
1150 * of elements in the dropzone (_ndropzone)
1151 * _pdropzone is not constrained. This is necessary
1152 * to allow moving multiple regions with y-distance
1155 * There can be 0 elements in the dropzone,
1156 * even though the drag-pointer is inside the DZ.
1159 * [ Audio-track, Midi-track, Audio-track, DZ ]
1160 * move regions from both audio tracks at the same time into the
1161 * DZ by grabbing the region in the bottom track.
1163 assert(current_pointer_time_axis_view >= 0);
1164 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1168 /* only move out of the zone if the movement is OK */
1169 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1170 assert(delta_time_axis_view < 0);
1171 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1172 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1173 * the current position can be calculated as follows:
1175 // a well placed oofus attack can still throw this off.
1176 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1177 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1180 /* last motion event was also over a time axis view */
1181 _last_pointer_time_axis_view += delta_time_axis_view;
1182 assert(_last_pointer_time_axis_view >= 0);
1187 /* the pointer is not over a time axis view */
1188 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1189 _pdropzone += delta_time_axis_view - delta_skip;
1190 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1193 _last_pointer_layer += delta_layer;
1197 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1199 if (_copy && first_move) {
1201 if (_x_constrained) {
1202 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1204 _editor->begin_reversible_command (Operations::region_copy);
1207 /* duplicate the regionview(s) and region(s) */
1209 list<DraggingView> new_regionviews;
1211 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1213 RegionView* rv = i->view;
1214 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1215 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1217 const boost::shared_ptr<const Region> original = rv->region();
1218 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1219 region_copy->set_position (original->position());
1220 /* need to set this so that the drop zone code can work. This doesn't
1221 actually put the region into the playlist, but just sets a weak pointer
1224 region_copy->set_playlist (original->playlist());
1228 boost::shared_ptr<AudioRegion> audioregion_copy
1229 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1231 nrv = new AudioRegionView (*arv, audioregion_copy);
1233 boost::shared_ptr<MidiRegion> midiregion_copy
1234 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1235 nrv = new MidiRegionView (*mrv, midiregion_copy);
1240 nrv->get_canvas_group()->show ();
1241 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1243 /* swap _primary to the copy */
1245 if (rv == _primary) {
1249 /* ..and deselect the one we copied */
1251 rv->set_selected (false);
1254 if (!new_regionviews.empty()) {
1256 /* reflect the fact that we are dragging the copies */
1258 _views = new_regionviews;
1260 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1263 } else if (!_copy && first_move) {
1265 if (_x_constrained) {
1266 _editor->begin_reversible_command (_("fixed time region drag"));
1268 _editor->begin_reversible_command (Operations::region_drag);
1272 RegionMotionDrag::motion (event, first_move);
1276 RegionMotionDrag::finished (GdkEvent *, bool)
1278 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1279 if (!(*i)->view()) {
1283 if ((*i)->view()->layer_display() == Expanded) {
1284 (*i)->view()->set_layer_display (Stacked);
1290 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1292 RegionMotionDrag::finished (ev, movement_occurred);
1294 if (!movement_occurred) {
1298 if (was_double_click() && !_views.empty()) {
1299 DraggingView dv = _views.front();
1300 dv.view->show_region_editor ();
1307 /* reverse this here so that we have the correct logic to finalize
1311 if (Config->get_edit_mode() == Lock) {
1312 _x_constrained = !_x_constrained;
1315 assert (!_views.empty ());
1317 /* We might have hidden region views so that they weren't visible during the drag
1318 (when they have been reparented). Now everything can be shown again, as region
1319 views are back in their track parent groups.
1321 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1322 i->view->get_canvas_group()->show ();
1325 bool const changed_position = (_last_frame_position != _primary->region()->position());
1326 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1327 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1347 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1351 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1353 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1358 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1359 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1360 uint32_t output_chan = region->n_channels();
1361 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1362 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1364 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name());
1365 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1367 rtav->set_height (original->current_height());
1371 ChanCount one_midi_port (DataType::MIDI, 1);
1372 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1373 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1374 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1376 rtav->set_height (original->current_height());
1381 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1387 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1389 RegionSelection new_views;
1390 PlaylistSet modified_playlists;
1391 RouteTimeAxisView* new_time_axis_view = 0;
1394 /* all changes were made during motion event handlers */
1396 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1400 _editor->commit_reversible_command ();
1404 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1405 PlaylistMapping playlist_mapping;
1407 /* insert the regions into their new playlists */
1408 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1410 RouteTimeAxisView* dest_rtv = 0;
1412 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1418 if (changed_position && !_x_constrained) {
1419 where = i->view->region()->position() - drag_delta;
1421 where = i->view->region()->position();
1424 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1425 /* dragged to drop zone */
1427 PlaylistMapping::iterator pm;
1429 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1430 /* first region from this original playlist: create a new track */
1431 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1432 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1433 dest_rtv = new_time_axis_view;
1435 /* we already created a new track for regions from this playlist, use it */
1436 dest_rtv = pm->second;
1439 /* destination time axis view is the one we dragged to */
1440 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1443 if (dest_rtv != 0) {
1444 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1445 if (new_view != 0) {
1446 new_views.push_back (new_view);
1450 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1451 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1454 list<DraggingView>::const_iterator next = i;
1460 /* If we've created new regions either by copying or moving
1461 to a new track, we want to replace the old selection with the new ones
1464 if (new_views.size() > 0) {
1465 _editor->selection->set (new_views);
1468 /* write commands for the accumulated diffs for all our modified playlists */
1469 add_stateful_diff_commands_for_playlists (modified_playlists);
1471 _editor->commit_reversible_command ();
1475 RegionMoveDrag::finished_no_copy (
1476 bool const changed_position,
1477 bool const changed_tracks,
1478 framecnt_t const drag_delta
1481 RegionSelection new_views;
1482 PlaylistSet modified_playlists;
1483 PlaylistSet frozen_playlists;
1484 set<RouteTimeAxisView*> views_to_update;
1485 RouteTimeAxisView* new_time_axis_view = 0;
1488 /* all changes were made during motion event handlers */
1489 _editor->commit_reversible_command ();
1493 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1494 PlaylistMapping playlist_mapping;
1496 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1498 RegionView* rv = i->view;
1499 RouteTimeAxisView* dest_rtv = 0;
1501 if (rv->region()->locked() || rv->region()->video_locked()) {
1506 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1507 /* dragged to drop zone */
1509 PlaylistMapping::iterator pm;
1511 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1512 /* first region from this original playlist: create a new track */
1513 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1514 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1515 dest_rtv = new_time_axis_view;
1517 /* we already created a new track for regions from this playlist, use it */
1518 dest_rtv = pm->second;
1522 /* destination time axis view is the one we dragged to */
1523 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1528 double const dest_layer = i->layer;
1530 views_to_update.insert (dest_rtv);
1534 if (changed_position && !_x_constrained) {
1535 where = rv->region()->position() - drag_delta;
1537 where = rv->region()->position();
1540 if (changed_tracks) {
1542 /* insert into new playlist */
1544 RegionView* new_view = insert_region_into_playlist (
1545 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1548 if (new_view == 0) {
1553 new_views.push_back (new_view);
1555 /* remove from old playlist */
1557 /* the region that used to be in the old playlist is not
1558 moved to the new one - we use a copy of it. as a result,
1559 any existing editor for the region should no longer be
1562 rv->hide_region_editor();
1565 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1569 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1571 /* this movement may result in a crossfade being modified, or a layering change,
1572 so we need to get undo data from the playlist as well as the region.
1575 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1577 playlist->clear_changes ();
1580 rv->region()->clear_changes ();
1583 motion on the same track. plonk the previously reparented region
1584 back to its original canvas group (its streamview).
1585 No need to do anything for copies as they are fake regions which will be deleted.
1588 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1589 rv->get_canvas_group()->set_y_position (i->initial_y);
1592 /* just change the model */
1593 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1594 playlist->set_layer (rv->region(), dest_layer);
1597 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1599 r = frozen_playlists.insert (playlist);
1602 playlist->freeze ();
1605 rv->region()->set_position (where);
1607 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1610 if (changed_tracks) {
1612 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1613 was selected in all of them, then removing it from a playlist will have removed all
1614 trace of it from _views (i.e. there were N regions selected, we removed 1,
1615 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1616 corresponding regionview, and _views is now empty).
1618 This could have invalidated any and all iterators into _views.
1620 The heuristic we use here is: if the region selection is empty, break out of the loop
1621 here. if the region selection is not empty, then restart the loop because we know that
1622 we must have removed at least the region(view) we've just been working on as well as any
1623 that we processed on previous iterations.
1625 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1626 we can just iterate.
1630 if (_views.empty()) {
1641 /* If we've created new regions either by copying or moving
1642 to a new track, we want to replace the old selection with the new ones
1645 if (new_views.size() > 0) {
1646 _editor->selection->set (new_views);
1649 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1653 /* write commands for the accumulated diffs for all our modified playlists */
1654 add_stateful_diff_commands_for_playlists (modified_playlists);
1656 _editor->commit_reversible_command ();
1658 /* We have futzed with the layering of canvas items on our streamviews.
1659 If any region changed layer, this will have resulted in the stream
1660 views being asked to set up their region views, and all will be well.
1661 If not, we might now have badly-ordered region views. Ask the StreamViews
1662 involved to sort themselves out, just in case.
1665 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1666 (*i)->view()->playlist_layered ((*i)->track ());
1670 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1671 * @param region Region to remove.
1672 * @param playlist playlist To remove from.
1673 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1674 * that clear_changes () is only called once per playlist.
1677 RegionMoveDrag::remove_region_from_playlist (
1678 boost::shared_ptr<Region> region,
1679 boost::shared_ptr<Playlist> playlist,
1680 PlaylistSet& modified_playlists
1683 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1686 playlist->clear_changes ();
1689 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1693 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1694 * clearing the playlist's diff history first if necessary.
1695 * @param region Region to insert.
1696 * @param dest_rtv Destination RouteTimeAxisView.
1697 * @param dest_layer Destination layer.
1698 * @param where Destination position.
1699 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1700 * that clear_changes () is only called once per playlist.
1701 * @return New RegionView, or 0 if no insert was performed.
1704 RegionMoveDrag::insert_region_into_playlist (
1705 boost::shared_ptr<Region> region,
1706 RouteTimeAxisView* dest_rtv,
1709 PlaylistSet& modified_playlists
1712 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1713 if (!dest_playlist) {
1717 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1718 _new_region_view = 0;
1719 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1721 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1722 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1724 dest_playlist->clear_changes ();
1727 dest_playlist->add_region (region, where);
1729 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1730 dest_playlist->set_layer (region, dest_layer);
1735 assert (_new_region_view);
1737 return _new_region_view;
1741 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1743 _new_region_view = rv;
1747 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1749 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1750 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1752 _editor->session()->add_command (c);
1761 RegionMoveDrag::aborted (bool movement_occurred)
1765 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1766 list<DraggingView>::const_iterator next = i;
1775 RegionMotionDrag::aborted (movement_occurred);
1780 RegionMotionDrag::aborted (bool)
1782 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1784 StreamView* sview = (*i)->view();
1787 if (sview->layer_display() == Expanded) {
1788 sview->set_layer_display (Stacked);
1793 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1794 RegionView* rv = i->view;
1795 TimeAxisView* tv = &(rv->get_time_axis_view ());
1796 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1798 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1799 rv->get_canvas_group()->set_y_position (0);
1801 rv->move (-_total_x_delta, 0);
1802 rv->set_height (rtv->view()->child_height ());
1806 /** @param b true to brush, otherwise false.
1807 * @param c true to make copies of the regions being moved, otherwise false.
1809 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1810 : RegionMotionDrag (e, i, p, v, b)
1813 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1816 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1817 if (rtv && rtv->is_track()) {
1818 speed = rtv->track()->speed ();
1821 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1825 RegionMoveDrag::setup_pointer_frame_offset ()
1827 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1830 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1831 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1833 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1835 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1836 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1838 _primary = v->view()->create_region_view (r, false, false);
1840 _primary->get_canvas_group()->show ();
1841 _primary->set_position (pos, 0);
1842 _views.push_back (DraggingView (_primary, this, v));
1844 _last_frame_position = pos;
1846 _item = _primary->get_canvas_group ();
1850 RegionInsertDrag::finished (GdkEvent *, bool)
1852 int pos = _views.front().time_axis_view;
1853 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1855 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1857 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1858 _primary->get_canvas_group()->set_y_position (0);
1860 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1862 _editor->begin_reversible_command (Operations::insert_region);
1863 playlist->clear_changes ();
1864 playlist->add_region (_primary->region (), _last_frame_position);
1866 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1867 if (Config->get_edit_mode() == Ripple) {
1868 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1871 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1872 _editor->commit_reversible_command ();
1880 RegionInsertDrag::aborted (bool)
1887 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1888 : RegionMoveDrag (e, i, p, v, false, false)
1890 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1893 struct RegionSelectionByPosition {
1894 bool operator() (RegionView*a, RegionView* b) {
1895 return a->region()->position () < b->region()->position();
1900 RegionSpliceDrag::motion (GdkEvent* event, bool)
1902 /* Which trackview is this ? */
1904 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1905 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1907 /* The region motion is only processed if the pointer is over
1911 if (!tv || !tv->is_track()) {
1912 /* To make sure we hide the verbose canvas cursor when the mouse is
1913 not held over an audio track.
1915 _editor->verbose_cursor()->hide ();
1918 _editor->verbose_cursor()->show ();
1923 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1929 RegionSelection copy;
1930 _editor->selection->regions.by_position(copy);
1932 framepos_t const pf = adjusted_current_frame (event);
1934 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1936 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1942 boost::shared_ptr<Playlist> playlist;
1944 if ((playlist = atv->playlist()) == 0) {
1948 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1953 if (pf < (*i)->region()->last_frame() + 1) {
1957 if (pf > (*i)->region()->first_frame()) {
1963 playlist->shuffle ((*i)->region(), dir);
1968 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1970 RegionMoveDrag::finished (event, movement_occurred);
1974 RegionSpliceDrag::aborted (bool)
1984 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1987 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1989 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1990 RegionSelection to_ripple;
1991 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1992 if ((*i)->position() >= where) {
1993 to_ripple.push_back (rtv->view()->find_view(*i));
1997 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1998 if (!exclude.contains (*i)) {
1999 // the selection has already been added to _views
2001 if (drag_in_progress) {
2002 // do the same things that RegionMotionDrag::motion does when
2003 // first_move is true, for the region views that we're adding
2004 // to _views this time
2007 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2008 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2009 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2010 rvg->reparent (_editor->_drag_motion_group);
2012 // we only need to move in the y direction
2013 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2018 _views.push_back (DraggingView (*i, this, tav));
2024 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2027 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2028 // we added all the regions after the selection
2030 std::list<DraggingView>::iterator to_erase = i++;
2031 if (!_editor->selection->regions.contains (to_erase->view)) {
2032 // restore the non-selected regions to their original playlist & positions,
2033 // and then ripple them back by the length of the regions that were dragged away
2034 // do the same things as RegionMotionDrag::aborted
2036 RegionView *rv = to_erase->view;
2037 TimeAxisView* tv = &(rv->get_time_axis_view ());
2038 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2041 // plonk them back onto their own track
2042 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2043 rv->get_canvas_group()->set_y_position (0);
2047 // move the underlying region to match the view
2048 rv->region()->set_position (rv->region()->position() + amount);
2050 // restore the view to match the underlying region's original position
2051 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2054 rv->set_height (rtv->view()->child_height ());
2055 _views.erase (to_erase);
2061 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2063 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2065 return allow_moves_across_tracks;
2073 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2074 : RegionMoveDrag (e, i, p, v, false, false)
2076 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2077 // compute length of selection
2078 RegionSelection selected_regions = _editor->selection->regions;
2079 selection_length = selected_regions.end_frame() - selected_regions.start();
2081 // we'll only allow dragging to another track in ripple mode if all the regions
2082 // being dragged start off on the same track
2083 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2086 exclude = new RegionList;
2087 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2088 exclude->push_back((*i)->region());
2091 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2092 RegionSelection copy;
2093 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2095 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2096 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2098 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2099 // find ripple start point on each applicable playlist
2100 RegionView *first_selected_on_this_track = NULL;
2101 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2102 if ((*i)->region()->playlist() == (*pi)) {
2103 // region is on this playlist - it's the first, because they're sorted
2104 first_selected_on_this_track = *i;
2108 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2109 add_all_after_to_views (
2110 &first_selected_on_this_track->get_time_axis_view(),
2111 first_selected_on_this_track->region()->position(),
2112 selected_regions, false);
2115 if (allow_moves_across_tracks) {
2116 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2124 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2126 /* Which trackview is this ? */
2128 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2129 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2131 /* The region motion is only processed if the pointer is over
2135 if (!tv || !tv->is_track()) {
2136 /* To make sure we hide the verbose canvas cursor when the mouse is
2137 not held over an audiotrack.
2139 _editor->verbose_cursor()->hide ();
2143 framepos_t where = adjusted_current_frame (event);
2144 assert (where >= 0);
2146 double delta = compute_x_delta (event, &after);
2148 framecnt_t amount = _editor->pixel_to_sample (delta);
2150 if (allow_moves_across_tracks) {
2151 // all the originally selected regions were on the same track
2153 framecnt_t adjust = 0;
2154 if (prev_tav && tv != prev_tav) {
2155 // dragged onto a different track
2156 // remove the unselected regions from _views, restore them to their original positions
2157 // and add the regions after the drop point on the new playlist to _views instead.
2158 // undo the effect of rippling the previous playlist, and include the effect of removing
2159 // the dragged region(s) from this track
2161 remove_unselected_from_views (prev_amount, false);
2162 // ripple previous playlist according to the regions that have been removed onto the new playlist
2163 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2166 // move just the selected regions
2167 RegionMoveDrag::motion(event, first_move);
2169 // ensure that the ripple operation on the new playlist inserts selection_length time
2170 adjust = selection_length;
2171 // ripple the new current playlist
2172 tv->playlist()->ripple (where, amount+adjust, exclude);
2174 // add regions after point where drag entered this track to subsequent ripples
2175 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2178 // motion on same track
2179 RegionMoveDrag::motion(event, first_move);
2183 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2184 prev_position = where;
2186 // selection encompasses multiple tracks - just drag
2187 // cross-track drags are forbidden
2188 RegionMoveDrag::motion(event, first_move);
2191 if (!_x_constrained) {
2192 prev_amount += amount;
2195 _last_frame_position = after;
2199 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2201 if (!movement_occurred) {
2205 if (was_double_click() && !_views.empty()) {
2206 DraggingView dv = _views.front();
2207 dv.view->show_region_editor ();
2214 _editor->begin_reversible_command(_("Ripple drag"));
2216 // remove the regions being rippled from the dragging view, updating them to
2217 // their new positions
2218 remove_unselected_from_views (prev_amount, true);
2220 if (allow_moves_across_tracks) {
2222 // if regions were dragged across tracks, we've rippled any later
2223 // regions on the track the regions were dragged off, so we need
2224 // to add the original track to the undo record
2225 orig_tav->playlist()->clear_changes();
2226 vector<Command*> cmds;
2227 orig_tav->playlist()->rdiff (cmds);
2228 _editor->session()->add_commands (cmds);
2230 if (prev_tav && prev_tav != orig_tav) {
2231 prev_tav->playlist()->clear_changes();
2232 vector<Command*> cmds;
2233 prev_tav->playlist()->rdiff (cmds);
2234 _editor->session()->add_commands (cmds);
2237 // selection spanned multiple tracks - all will need adding to undo record
2239 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2240 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2242 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2243 (*pi)->clear_changes();
2244 vector<Command*> cmds;
2245 (*pi)->rdiff (cmds);
2246 _editor->session()->add_commands (cmds);
2250 // other modified playlists are added to undo by RegionMoveDrag::finished()
2251 RegionMoveDrag::finished (event, movement_occurred);
2252 _editor->commit_reversible_command();
2256 RegionRippleDrag::aborted (bool movement_occurred)
2258 RegionMoveDrag::aborted (movement_occurred);
2263 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2265 _view (dynamic_cast<MidiTimeAxisView*> (v))
2267 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2273 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2276 _region = add_midi_region (_view);
2277 _view->playlist()->freeze ();
2280 framepos_t const f = adjusted_current_frame (event);
2281 if (f < grab_frame()) {
2282 _region->set_position (f);
2285 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2286 so that if this region is duplicated, its duplicate starts on
2287 a snap point rather than 1 frame after a snap point. Otherwise things get
2288 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2289 place snapped notes at the start of the region.
2292 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2293 _region->set_length (len < 1 ? 1 : len);
2299 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2301 if (!movement_occurred) {
2302 add_midi_region (_view);
2304 _view->playlist()->thaw ();
2309 RegionCreateDrag::aborted (bool)
2312 _view->playlist()->thaw ();
2318 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2322 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2326 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2328 Gdk::Cursor* cursor;
2329 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2331 float x_fraction = cnote->mouse_x_fraction ();
2333 if (x_fraction > 0.0 && x_fraction < 0.25) {
2334 cursor = _editor->cursors()->left_side_trim;
2337 cursor = _editor->cursors()->right_side_trim;
2341 Drag::start_grab (event, cursor);
2343 region = &cnote->region_view();
2347 if (event->motion.state & Keyboard::PrimaryModifier) {
2353 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2355 if (ms.size() > 1) {
2356 /* has to be relative, may make no sense otherwise */
2360 /* select this note; if it is already selected, preserve the existing selection,
2361 otherwise make this note the only one selected.
2363 region->note_selected (cnote, cnote->selected ());
2365 _editor->begin_reversible_command (_("resize notes"));
2367 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2368 MidiRegionSelection::iterator next;
2371 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2373 mrv->begin_resizing (at_front);
2380 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
2382 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2383 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2384 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2386 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2388 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2394 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
2396 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2397 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2398 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2400 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2402 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2406 _editor->commit_reversible_command ();
2410 NoteResizeDrag::aborted (bool)
2412 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2413 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2414 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2416 mrv->abort_resizing ();
2421 AVDraggingView::AVDraggingView (RegionView* v)
2424 initial_position = v->region()->position ();
2427 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2430 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2433 TrackViewList empty;
2435 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2436 std::list<RegionView*> views = rs.by_layer();
2438 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2439 RegionView* rv = (*i);
2440 if (!rv->region()->video_locked()) {
2443 _views.push_back (AVDraggingView (rv));
2448 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2450 Drag::start_grab (event);
2451 if (_editor->session() == 0) {
2455 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2456 _max_backwards_drag = (
2457 ARDOUR_UI::instance()->video_timeline->get_duration()
2458 + ARDOUR_UI::instance()->video_timeline->get_offset()
2459 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2462 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2463 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2464 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2467 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2470 Timecode::Time timecode;
2471 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2472 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);
2473 show_verbose_cursor_text (buf);
2477 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2479 if (_editor->session() == 0) {
2482 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2486 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2487 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2489 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2490 dt = - _max_backwards_drag;
2493 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2494 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2496 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2497 RegionView* rv = i->view;
2498 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2501 rv->region()->clear_changes ();
2502 rv->region()->suspend_property_changes();
2504 rv->region()->set_position(i->initial_position + dt);
2505 rv->region_changed(ARDOUR::Properties::position);
2508 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2509 Timecode::Time timecode;
2510 Timecode::Time timediff;
2512 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2513 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2514 snprintf (buf, sizeof (buf),
2515 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2516 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2517 , _("Video Start:"),
2518 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2520 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2522 show_verbose_cursor_text (buf);
2526 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2528 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2532 if (!movement_occurred || ! _editor->session()) {
2536 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2538 _editor->begin_reversible_command (_("Move Video"));
2540 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2541 ARDOUR_UI::instance()->video_timeline->save_undo();
2542 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2543 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2545 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2546 i->view->drag_end();
2547 i->view->region()->resume_property_changes ();
2549 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2552 _editor->session()->maybe_update_session_range(
2553 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2554 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2558 _editor->commit_reversible_command ();
2562 VideoTimeLineDrag::aborted (bool)
2564 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2567 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2568 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2570 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2571 i->view->region()->resume_property_changes ();
2572 i->view->region()->set_position(i->initial_position);
2576 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2577 : RegionDrag (e, i, p, v)
2578 , _preserve_fade_anchor (preserve_fade_anchor)
2579 , _jump_position_when_done (false)
2581 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2585 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2588 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2589 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2591 if (tv && tv->is_track()) {
2592 speed = tv->track()->speed();
2595 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2596 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2597 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2599 framepos_t const pf = adjusted_current_frame (event);
2601 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2602 /* Move the contents of the region around without changing the region bounds */
2603 _operation = ContentsTrim;
2604 Drag::start_grab (event, _editor->cursors()->trimmer);
2606 /* These will get overridden for a point trim.*/
2607 if (pf < (region_start + region_length/2)) {
2608 /* closer to front */
2609 _operation = StartTrim;
2611 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2612 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2614 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2618 _operation = EndTrim;
2619 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2620 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2622 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2627 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2628 _jump_position_when_done = true;
2631 switch (_operation) {
2633 show_verbose_cursor_time (region_start);
2634 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2635 i->view->trim_front_starting ();
2639 show_verbose_cursor_time (region_end);
2642 show_verbose_cursor_time (pf);
2646 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2647 i->view->region()->suspend_property_changes ();
2652 TrimDrag::motion (GdkEvent* event, bool first_move)
2654 RegionView* rv = _primary;
2657 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2658 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2659 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2660 frameoffset_t frame_delta = 0;
2662 if (tv && tv->is_track()) {
2663 speed = tv->track()->speed();
2666 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2672 switch (_operation) {
2674 trim_type = "Region start trim";
2677 trim_type = "Region end trim";
2680 trim_type = "Region content trim";
2687 _editor->begin_reversible_command (trim_type);
2689 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2690 RegionView* rv = i->view;
2691 rv->enable_display (false);
2692 rv->region()->playlist()->clear_owned_changes ();
2694 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2697 arv->temporarily_hide_envelope ();
2701 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2702 insert_result = _editor->motion_frozen_playlists.insert (pl);
2704 if (insert_result.second) {
2710 bool non_overlap_trim = false;
2712 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2713 non_overlap_trim = true;
2716 /* contstrain trim to fade length */
2717 if (_preserve_fade_anchor) {
2718 switch (_operation) {
2720 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2721 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2723 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2724 if (ar->locked()) continue;
2725 framecnt_t len = ar->fade_in()->back()->when;
2726 if (len < dt) dt = min(dt, len);
2730 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2731 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2733 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2734 if (ar->locked()) continue;
2735 framecnt_t len = ar->fade_out()->back()->when;
2736 if (len < -dt) dt = max(dt, -len);
2745 switch (_operation) {
2747 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2748 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2749 if (changed && _preserve_fade_anchor) {
2750 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2752 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2753 framecnt_t len = ar->fade_in()->back()->when;
2754 framecnt_t diff = ar->first_frame() - i->initial_position;
2755 framepos_t new_length = len - diff;
2756 i->anchored_fade_length = min (ar->length(), new_length);
2757 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2758 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2765 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2766 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2767 if (changed && _preserve_fade_anchor) {
2768 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2770 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2771 framecnt_t len = ar->fade_out()->back()->when;
2772 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2773 framepos_t new_length = len + diff;
2774 i->anchored_fade_length = min (ar->length(), new_length);
2775 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2776 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2784 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2786 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2787 i->view->move_contents (frame_delta);
2793 switch (_operation) {
2795 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2798 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2801 // show_verbose_cursor_time (frame_delta);
2808 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2810 if (movement_occurred) {
2811 motion (event, false);
2813 if (_operation == StartTrim) {
2814 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2816 /* This must happen before the region's StatefulDiffCommand is created, as it may
2817 `correct' (ahem) the region's _start from being negative to being zero. It
2818 needs to be zero in the undo record.
2820 i->view->trim_front_ending ();
2822 if (_preserve_fade_anchor) {
2823 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2825 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2826 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2827 ar->set_fade_in_length(i->anchored_fade_length);
2828 ar->set_fade_in_active(true);
2831 if (_jump_position_when_done) {
2832 i->view->region()->set_position (i->initial_position);
2835 } else if (_operation == EndTrim) {
2836 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2837 if (_preserve_fade_anchor) {
2838 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2840 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2841 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2842 ar->set_fade_out_length(i->anchored_fade_length);
2843 ar->set_fade_out_active(true);
2846 if (_jump_position_when_done) {
2847 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2852 if (!_views.empty()) {
2853 if (_operation == StartTrim) {
2854 _editor->maybe_locate_with_edit_preroll(
2855 _views.begin()->view->region()->position());
2857 if (_operation == EndTrim) {
2858 _editor->maybe_locate_with_edit_preroll(
2859 _views.begin()->view->region()->position() +
2860 _views.begin()->view->region()->length());
2864 if (!_editor->selection->selected (_primary)) {
2865 _primary->thaw_after_trim ();
2868 set<boost::shared_ptr<Playlist> > diffed_playlists;
2870 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2871 i->view->thaw_after_trim ();
2872 i->view->enable_display (true);
2874 /* Trimming one region may affect others on the playlist, so we need
2875 to get undo Commands from the whole playlist rather than just the
2876 region. Use diffed_playlists to make sure we don't diff a given
2877 playlist more than once.
2879 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2880 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2881 vector<Command*> cmds;
2883 _editor->session()->add_commands (cmds);
2884 diffed_playlists.insert (p);
2889 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2893 _editor->motion_frozen_playlists.clear ();
2894 _editor->commit_reversible_command();
2897 /* no mouse movement */
2898 _editor->point_trim (event, adjusted_current_frame (event));
2901 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2902 if (_operation == StartTrim) {
2903 i->view->trim_front_ending ();
2906 i->view->region()->resume_property_changes ();
2911 TrimDrag::aborted (bool movement_occurred)
2913 /* Our motion method is changing model state, so use the Undo system
2914 to cancel. Perhaps not ideal, as this will leave an Undo point
2915 behind which may be slightly odd from the user's point of view.
2920 if (movement_occurred) {
2924 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2925 i->view->region()->resume_property_changes ();
2930 TrimDrag::setup_pointer_frame_offset ()
2932 list<DraggingView>::iterator i = _views.begin ();
2933 while (i != _views.end() && i->view != _primary) {
2937 if (i == _views.end()) {
2941 switch (_operation) {
2943 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2946 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2953 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2957 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2958 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2963 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2965 Drag::start_grab (event, cursor);
2966 show_verbose_cursor_time (adjusted_current_frame(event));
2970 MeterMarkerDrag::setup_pointer_frame_offset ()
2972 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2976 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2978 if (!_marker->meter().movable()) {
2984 // create a dummy marker for visual representation of moving the
2985 // section, because whether its a copy or not, we're going to
2986 // leave or lose the original marker (leave if its a copy; lose if its
2987 // not, because we'll remove it from the map).
2989 MeterSection section (_marker->meter());
2991 if (!section.movable()) {
2996 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2998 _marker = new MeterMarker (
3000 *_editor->meter_group,
3001 ARDOUR_UI::config()->color ("meter marker"),
3003 *new MeterSection (_marker->meter())
3006 /* use the new marker for the grab */
3007 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3010 TempoMap& map (_editor->session()->tempo_map());
3011 /* get current state */
3012 before_state = &map.get_state();
3013 /* remove the section while we drag it */
3014 map.remove_meter (section, true);
3018 framepos_t const pf = adjusted_current_frame (event);
3020 _marker->set_position (pf);
3021 show_verbose_cursor_time (pf);
3025 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3027 if (!movement_occurred) {
3028 if (was_double_click()) {
3029 _editor->edit_meter_marker (*_marker);
3034 if (!_marker->meter().movable()) {
3038 motion (event, false);
3040 Timecode::BBT_Time when;
3042 TempoMap& map (_editor->session()->tempo_map());
3043 map.bbt_time (last_pointer_frame(), when);
3045 if (_copy == true) {
3046 _editor->begin_reversible_command (_("copy meter mark"));
3047 XMLNode &before = map.get_state();
3048 map.add_meter (_marker->meter(), when);
3049 XMLNode &after = map.get_state();
3050 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3051 _editor->commit_reversible_command ();
3054 _editor->begin_reversible_command (_("move meter mark"));
3056 /* we removed it before, so add it back now */
3058 map.add_meter (_marker->meter(), when);
3059 XMLNode &after = map.get_state();
3060 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3061 _editor->commit_reversible_command ();
3064 // delete the dummy marker we used for visual representation while moving.
3065 // a new visual marker will show up automatically.
3070 MeterMarkerDrag::aborted (bool moved)
3072 _marker->set_position (_marker->meter().frame ());
3075 TempoMap& map (_editor->session()->tempo_map());
3076 /* we removed it before, so add it back now */
3077 map.add_meter (_marker->meter(), _marker->meter().frame());
3078 // delete the dummy marker we used for visual representation while moving.
3079 // a new visual marker will show up automatically.
3084 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3088 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3090 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3095 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3097 Drag::start_grab (event, cursor);
3098 show_verbose_cursor_time (adjusted_current_frame (event));
3102 TempoMarkerDrag::setup_pointer_frame_offset ()
3104 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3108 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3110 if (!_marker->tempo().movable()) {
3116 // create a dummy marker for visual representation of moving the
3117 // section, because whether its a copy or not, we're going to
3118 // leave or lose the original marker (leave if its a copy; lose if its
3119 // not, because we'll remove it from the map).
3121 // create a dummy marker for visual representation of moving the copy.
3122 // The actual copying is not done before we reach the finish callback.
3125 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3127 TempoSection section (_marker->tempo());
3129 _marker = new TempoMarker (
3131 *_editor->tempo_group,
3132 ARDOUR_UI::config()->color ("tempo marker"),
3134 *new TempoSection (_marker->tempo())
3137 /* use the new marker for the grab */
3138 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3141 TempoMap& map (_editor->session()->tempo_map());
3142 /* get current state */
3143 before_state = &map.get_state();
3144 /* remove the section while we drag it */
3145 map.remove_tempo (section, true);
3149 framepos_t const pf = adjusted_current_frame (event);
3150 _marker->set_position (pf);
3151 show_verbose_cursor_time (pf);
3155 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3157 if (!movement_occurred) {
3158 if (was_double_click()) {
3159 _editor->edit_tempo_marker (*_marker);
3164 if (!_marker->tempo().movable()) {
3168 motion (event, false);
3170 TempoMap& map (_editor->session()->tempo_map());
3171 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3172 Timecode::BBT_Time when;
3174 map.bbt_time (beat_time, when);
3176 if (_copy == true) {
3177 _editor->begin_reversible_command (_("copy tempo mark"));
3178 XMLNode &before = map.get_state();
3179 map.add_tempo (_marker->tempo(), when);
3180 XMLNode &after = map.get_state();
3181 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3182 _editor->commit_reversible_command ();
3185 _editor->begin_reversible_command (_("move tempo mark"));
3186 /* we removed it before, so add it back now */
3187 map.add_tempo (_marker->tempo(), when);
3188 XMLNode &after = map.get_state();
3189 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3190 _editor->commit_reversible_command ();
3193 // delete the dummy marker we used for visual representation while moving.
3194 // a new visual marker will show up automatically.
3199 TempoMarkerDrag::aborted (bool moved)
3201 _marker->set_position (_marker->tempo().frame());
3203 TempoMap& map (_editor->session()->tempo_map());
3204 /* we removed it before, so add it back now */
3205 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3206 // delete the dummy marker we used for visual representation while moving.
3207 // a new visual marker will show up automatically.
3212 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3213 : Drag (e, &c.track_canvas_item(), false)
3217 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3220 /** Do all the things we do when dragging the playhead to make it look as though
3221 * we have located, without actually doing the locate (because that would cause
3222 * the diskstream buffers to be refilled, which is too slow).
3225 CursorDrag::fake_locate (framepos_t t)
3227 _editor->playhead_cursor->set_position (t);
3229 Session* s = _editor->session ();
3230 if (s->timecode_transmission_suspended ()) {
3231 framepos_t const f = _editor->playhead_cursor->current_frame ();
3232 /* This is asynchronous so it will be sent "now"
3234 s->send_mmc_locate (f);
3235 /* These are synchronous and will be sent during the next
3238 s->queue_full_time_code ();
3239 s->queue_song_position_pointer ();
3242 show_verbose_cursor_time (t);
3243 _editor->UpdateAllTransportClocks (t);
3247 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3249 Drag::start_grab (event, c);
3251 _grab_zoom = _editor->samples_per_pixel;
3253 framepos_t where = _editor->canvas_event_sample (event);
3255 _editor->snap_to_with_modifier (where, event);
3257 _editor->_dragging_playhead = true;
3259 Session* s = _editor->session ();
3261 /* grab the track canvas item as well */
3263 _cursor.track_canvas_item().grab();
3266 if (_was_rolling && _stop) {
3270 if (s->is_auditioning()) {
3271 s->cancel_audition ();
3275 if (AudioEngine::instance()->connected()) {
3277 /* do this only if we're the engine is connected
3278 * because otherwise this request will never be
3279 * serviced and we'll busy wait forever. likewise,
3280 * notice if we are disconnected while waiting for the
3281 * request to be serviced.
3284 s->request_suspend_timecode_transmission ();
3285 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3286 /* twiddle our thumbs */
3291 fake_locate (where);
3295 CursorDrag::motion (GdkEvent* event, bool)
3297 framepos_t const adjusted_frame = adjusted_current_frame (event);
3298 if (adjusted_frame != last_pointer_frame()) {
3299 fake_locate (adjusted_frame);
3304 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3306 _editor->_dragging_playhead = false;
3308 _cursor.track_canvas_item().ungrab();
3310 if (!movement_occurred && _stop) {
3314 motion (event, false);
3316 Session* s = _editor->session ();
3318 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3319 _editor->_pending_locate_request = true;
3320 s->request_resume_timecode_transmission ();
3325 CursorDrag::aborted (bool)
3327 _cursor.track_canvas_item().ungrab();
3329 if (_editor->_dragging_playhead) {
3330 _editor->session()->request_resume_timecode_transmission ();
3331 _editor->_dragging_playhead = false;
3334 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3337 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3338 : RegionDrag (e, i, p, v)
3340 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3344 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3346 Drag::start_grab (event, cursor);
3348 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3349 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3351 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3355 FadeInDrag::setup_pointer_frame_offset ()
3357 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3358 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3359 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3363 FadeInDrag::motion (GdkEvent* event, bool)
3365 framecnt_t fade_length;
3366 framepos_t const pos = adjusted_current_frame (event);
3367 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3369 if (pos < (region->position() + 64)) {
3370 fade_length = 64; // this should be a minimum defined somewhere
3371 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3372 fade_length = region->length() - region->fade_out()->back()->when - 1;
3374 fade_length = pos - region->position();
3377 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3379 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3385 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3388 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3392 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3394 if (!movement_occurred) {
3398 framecnt_t fade_length;
3400 framepos_t const pos = adjusted_current_frame (event);
3402 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3404 if (pos < (region->position() + 64)) {
3405 fade_length = 64; // this should be a minimum defined somewhere
3406 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3407 fade_length = region->length() - region->fade_out()->back()->when - 1;
3409 fade_length = pos - region->position();
3412 _editor->begin_reversible_command (_("change fade in length"));
3414 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3416 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3422 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3423 XMLNode &before = alist->get_state();
3425 tmp->audio_region()->set_fade_in_length (fade_length);
3426 tmp->audio_region()->set_fade_in_active (true);
3428 XMLNode &after = alist->get_state();
3429 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3432 _editor->commit_reversible_command ();
3436 FadeInDrag::aborted (bool)
3438 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3439 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3445 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3449 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3450 : RegionDrag (e, i, p, v)
3452 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3456 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3458 Drag::start_grab (event, cursor);
3460 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3461 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3463 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3467 FadeOutDrag::setup_pointer_frame_offset ()
3469 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3470 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3471 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3475 FadeOutDrag::motion (GdkEvent* event, bool)
3477 framecnt_t fade_length;
3479 framepos_t const pos = adjusted_current_frame (event);
3481 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3483 if (pos > (region->last_frame() - 64)) {
3484 fade_length = 64; // this should really be a minimum fade defined somewhere
3485 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3486 fade_length = region->length() - region->fade_in()->back()->when - 1;
3488 fade_length = region->last_frame() - pos;
3491 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3493 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3499 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3502 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3506 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3508 if (!movement_occurred) {
3512 framecnt_t fade_length;
3514 framepos_t const pos = adjusted_current_frame (event);
3516 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3518 if (pos > (region->last_frame() - 64)) {
3519 fade_length = 64; // this should really be a minimum fade defined somewhere
3520 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3521 fade_length = region->length() - region->fade_in()->back()->when - 1;
3523 fade_length = region->last_frame() - pos;
3526 _editor->begin_reversible_command (_("change fade out length"));
3528 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3530 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3536 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3537 XMLNode &before = alist->get_state();
3539 tmp->audio_region()->set_fade_out_length (fade_length);
3540 tmp->audio_region()->set_fade_out_active (true);
3542 XMLNode &after = alist->get_state();
3543 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3546 _editor->commit_reversible_command ();
3550 FadeOutDrag::aborted (bool)
3552 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3553 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3559 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3563 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3566 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3568 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3571 _points.push_back (ArdourCanvas::Duple (0, 0));
3572 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3575 MarkerDrag::~MarkerDrag ()
3577 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3582 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3584 location = new Location (*l);
3585 markers.push_back (m);
3590 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3592 Drag::start_grab (event, cursor);
3596 Location *location = _editor->find_location_from_marker (_marker, is_start);
3597 _editor->_dragging_edit_point = true;
3599 update_item (location);
3601 // _drag_line->show();
3602 // _line->raise_to_top();
3605 show_verbose_cursor_time (location->start());
3607 show_verbose_cursor_time (location->end());
3610 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3613 case Selection::Toggle:
3614 /* we toggle on the button release */
3616 case Selection::Set:
3617 if (!_editor->selection->selected (_marker)) {
3618 _editor->selection->set (_marker);
3621 case Selection::Extend:
3623 Locations::LocationList ll;
3624 list<Marker*> to_add;
3626 _editor->selection->markers.range (s, e);
3627 s = min (_marker->position(), s);
3628 e = max (_marker->position(), e);
3631 if (e < max_framepos) {
3634 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3635 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3636 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3639 to_add.push_back (lm->start);
3642 to_add.push_back (lm->end);
3646 if (!to_add.empty()) {
3647 _editor->selection->add (to_add);
3651 case Selection::Add:
3652 _editor->selection->add (_marker);
3656 /* Set up copies for us to manipulate during the drag
3659 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3661 Location* l = _editor->find_location_from_marker (*i, is_start);
3668 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3670 /* range: check that the other end of the range isn't
3673 CopiedLocationInfo::iterator x;
3674 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3675 if (*(*x).location == *l) {
3679 if (x == _copied_locations.end()) {
3680 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3682 (*x).markers.push_back (*i);
3683 (*x).move_both = true;
3691 MarkerDrag::setup_pointer_frame_offset ()
3694 Location *location = _editor->find_location_from_marker (_marker, is_start);
3695 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3699 MarkerDrag::motion (GdkEvent* event, bool)
3701 framecnt_t f_delta = 0;
3703 bool move_both = false;
3704 Location *real_location;
3705 Location *copy_location = 0;
3707 framepos_t const newframe = adjusted_current_frame (event);
3708 framepos_t next = newframe;
3710 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3714 CopiedLocationInfo::iterator x;
3716 /* find the marker we're dragging, and compute the delta */
3718 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3720 copy_location = (*x).location;
3722 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3724 /* this marker is represented by this
3725 * CopiedLocationMarkerInfo
3728 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3733 if (real_location->is_mark()) {
3734 f_delta = newframe - copy_location->start();
3738 switch (_marker->type()) {
3739 case Marker::SessionStart:
3740 case Marker::RangeStart:
3741 case Marker::LoopStart:
3742 case Marker::PunchIn:
3743 f_delta = newframe - copy_location->start();
3746 case Marker::SessionEnd:
3747 case Marker::RangeEnd:
3748 case Marker::LoopEnd:
3749 case Marker::PunchOut:
3750 f_delta = newframe - copy_location->end();
3753 /* what kind of marker is this ? */
3762 if (x == _copied_locations.end()) {
3763 /* hmm, impossible - we didn't find the dragged marker */
3767 /* now move them all */
3769 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3771 copy_location = x->location;
3773 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3777 if (real_location->locked()) {
3781 if (copy_location->is_mark()) {
3785 copy_location->set_start (copy_location->start() + f_delta);
3789 framepos_t new_start = copy_location->start() + f_delta;
3790 framepos_t new_end = copy_location->end() + f_delta;
3792 if (is_start) { // start-of-range marker
3794 if (move_both || (*x).move_both) {
3795 copy_location->set_start (new_start);
3796 copy_location->set_end (new_end);
3797 } else if (new_start < copy_location->end()) {
3798 copy_location->set_start (new_start);
3799 } else if (newframe > 0) {
3800 _editor->snap_to (next, RoundUpAlways, true);
3801 copy_location->set_end (next);
3802 copy_location->set_start (newframe);
3805 } else { // end marker
3807 if (move_both || (*x).move_both) {
3808 copy_location->set_end (new_end);
3809 copy_location->set_start (new_start);
3810 } else if (new_end > copy_location->start()) {
3811 copy_location->set_end (new_end);
3812 } else if (newframe > 0) {
3813 _editor->snap_to (next, RoundDownAlways, true);
3814 copy_location->set_start (next);
3815 copy_location->set_end (newframe);
3820 update_item (copy_location);
3822 /* now lookup the actual GUI items used to display this
3823 * location and move them to wherever the copy of the location
3824 * is now. This means that the logic in ARDOUR::Location is
3825 * still enforced, even though we are not (yet) modifying
3826 * the real Location itself.
3829 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3832 lm->set_position (copy_location->start(), copy_location->end());
3837 assert (!_copied_locations.empty());
3839 show_verbose_cursor_time (newframe);
3843 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3845 if (!movement_occurred) {
3847 if (was_double_click()) {
3848 _editor->rename_marker (_marker);
3852 /* just a click, do nothing but finish
3853 off the selection process
3856 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3859 case Selection::Set:
3860 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3861 _editor->selection->set (_marker);
3865 case Selection::Toggle:
3866 /* we toggle on the button release, click only */
3867 _editor->selection->toggle (_marker);
3870 case Selection::Extend:
3871 case Selection::Add:
3878 _editor->_dragging_edit_point = false;
3880 _editor->begin_reversible_command ( _("move marker") );
3881 XMLNode &before = _editor->session()->locations()->get_state();
3883 MarkerSelection::iterator i;
3884 CopiedLocationInfo::iterator x;
3887 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3888 x != _copied_locations.end() && i != _editor->selection->markers.end();
3891 Location * location = _editor->find_location_from_marker (*i, is_start);
3895 if (location->locked()) {
3899 if (location->is_mark()) {
3900 location->set_start (((*x).location)->start());
3902 location->set (((*x).location)->start(), ((*x).location)->end());
3907 XMLNode &after = _editor->session()->locations()->get_state();
3908 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3909 _editor->commit_reversible_command ();
3913 MarkerDrag::aborted (bool movement_occured)
3915 if (!movement_occured) {
3919 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3921 /* move all markers to their original location */
3924 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3927 Location * location = _editor->find_location_from_marker (*m, is_start);
3930 (*m)->set_position (is_start ? location->start() : location->end());
3937 MarkerDrag::update_item (Location*)
3942 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3944 _cumulative_x_drag (0),
3945 _cumulative_y_drag (0)
3947 if (_zero_gain_fraction < 0.0) {
3948 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3951 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3953 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3959 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3961 Drag::start_grab (event, _editor->cursors()->fader);
3963 // start the grab at the center of the control point so
3964 // the point doesn't 'jump' to the mouse after the first drag
3965 _fixed_grab_x = _point->get_x();
3966 _fixed_grab_y = _point->get_y();
3968 float const fraction = 1 - (_point->get_y() / _point->line().height());
3970 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3972 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3974 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3976 if (!_point->can_slide ()) {
3977 _x_constrained = true;
3982 ControlPointDrag::motion (GdkEvent* event, bool)
3984 double dx = _drags->current_pointer_x() - last_pointer_x();
3985 double dy = current_pointer_y() - last_pointer_y();
3987 if (event->button.state & Keyboard::SecondaryModifier) {
3992 /* coordinate in pixels relative to the start of the region (for region-based automation)
3993 or track (for track-based automation) */
3994 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3995 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3997 // calculate zero crossing point. back off by .01 to stay on the
3998 // positive side of zero
3999 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4001 // make sure we hit zero when passing through
4002 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4006 if (_x_constrained) {
4009 if (_y_constrained) {
4013 _cumulative_x_drag = cx - _fixed_grab_x;
4014 _cumulative_y_drag = cy - _fixed_grab_y;
4018 cy = min ((double) _point->line().height(), cy);
4020 framepos_t cx_frames = _editor->pixel_to_sample (cx);
4022 if (!_x_constrained) {
4023 _editor->snap_to_with_modifier (cx_frames, event);
4026 cx_frames = min (cx_frames, _point->line().maximum_time());
4028 float const fraction = 1.0 - (cy / _point->line().height());
4030 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4032 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4036 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4038 if (!movement_occurred) {
4042 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4043 _editor->reset_point_selection ();
4047 motion (event, false);
4050 _point->line().end_drag (_pushing, _final_index);
4051 _editor->commit_reversible_command ();
4055 ControlPointDrag::aborted (bool)
4057 _point->line().reset ();
4061 ControlPointDrag::active (Editing::MouseMode m)
4063 if (m == Editing::MouseDraw) {
4064 /* always active in mouse draw */
4068 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4069 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4072 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4075 _cumulative_y_drag (0)
4077 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4081 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4083 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4086 _item = &_line->grab_item ();
4088 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4089 origin, and ditto for y.
4092 double cx = event->button.x;
4093 double cy = event->button.y;
4095 _line->parent_group().canvas_to_item (cx, cy);
4097 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4102 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
4103 /* no adjacent points */
4107 Drag::start_grab (event, _editor->cursors()->fader);
4109 /* store grab start in parent frame */
4114 double fraction = 1.0 - (cy / _line->height());
4116 _line->start_drag_line (before, after, fraction);
4118 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4122 LineDrag::motion (GdkEvent* event, bool)
4124 double dy = current_pointer_y() - last_pointer_y();
4126 if (event->button.state & Keyboard::SecondaryModifier) {
4130 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4132 _cumulative_y_drag = cy - _fixed_grab_y;
4135 cy = min ((double) _line->height(), cy);
4137 double const fraction = 1.0 - (cy / _line->height());
4140 /* we are ignoring x position for this drag, so we can just pass in anything */
4141 _line->drag_motion (0, fraction, true, false, ignored);
4143 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4147 LineDrag::finished (GdkEvent* event, bool movement_occured)
4149 if (movement_occured) {
4150 motion (event, false);
4151 _line->end_drag (false, 0);
4153 /* add a new control point on the line */
4155 AutomationTimeAxisView* atv;
4157 _line->end_drag (false, 0);
4159 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4160 framepos_t where = _editor->window_event_sample (event, 0, 0);
4161 atv->add_automation_event (event, where, event->button.y, false);
4165 _editor->commit_reversible_command ();
4169 LineDrag::aborted (bool)
4174 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4177 _cumulative_x_drag (0)
4179 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4183 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4185 Drag::start_grab (event);
4187 _line = reinterpret_cast<Line*> (_item);
4190 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4192 double cx = event->button.x;
4193 double cy = event->button.y;
4195 _item->parent()->canvas_to_item (cx, cy);
4197 /* store grab start in parent frame */
4198 _region_view_grab_x = cx;
4200 _before = *(float*) _item->get_data ("position");
4202 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4204 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4208 FeatureLineDrag::motion (GdkEvent*, bool)
4210 double dx = _drags->current_pointer_x() - last_pointer_x();
4212 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4214 _cumulative_x_drag += dx;
4216 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4225 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4227 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4229 float *pos = new float;
4232 _line->set_data ("position", pos);
4238 FeatureLineDrag::finished (GdkEvent*, bool)
4240 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4241 _arv->update_transient(_before, _before);
4245 FeatureLineDrag::aborted (bool)
4250 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4252 , _vertical_only (false)
4254 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4258 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4260 Drag::start_grab (event);
4261 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4265 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4272 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4274 framepos_t grab = grab_frame ();
4275 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4276 _editor->snap_to_with_modifier (grab, event);
4278 grab = raw_grab_frame ();
4281 /* base start and end on initial click position */
4291 if (current_pointer_y() < grab_y()) {
4292 y1 = current_pointer_y();
4295 y2 = current_pointer_y();
4299 if (start != end || y1 != y2) {
4301 double x1 = _editor->sample_to_pixel (start);
4302 double x2 = _editor->sample_to_pixel (end);
4303 const double min_dimension = 2.0;
4305 if (_vertical_only) {
4306 /* fixed 10 pixel width */
4310 x2 = min (x1 - min_dimension, x2);
4312 x2 = max (x1 + min_dimension, x2);
4317 y2 = min (y1 - min_dimension, y2);
4319 y2 = max (y1 + min_dimension, y2);
4322 /* translate rect into item space and set */
4324 ArdourCanvas::Rect r (x1, y1, x2, y2);
4326 /* this drag is a _trackview_only == true drag, so the y1 and
4327 * y2 (computed using current_pointer_y() and grab_y()) will be
4328 * relative to the top of the trackview group). The
4329 * rubberband rect has the same parent/scroll offset as the
4330 * the trackview group, so we can use the "r" rect directly
4331 * to set the shape of the rubberband.
4334 _editor->rubberband_rect->set (r);
4335 _editor->rubberband_rect->show();
4336 _editor->rubberband_rect->raise_to_top();
4338 show_verbose_cursor_time (pf);
4340 do_select_things (event, true);
4345 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4349 framepos_t grab = grab_frame ();
4350 framepos_t lpf = last_pointer_frame ();
4352 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4353 grab = raw_grab_frame ();
4354 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4368 if (current_pointer_y() < grab_y()) {
4369 y1 = current_pointer_y();
4372 y2 = current_pointer_y();
4376 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4380 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4382 if (movement_occurred) {
4384 motion (event, false);
4385 do_select_things (event, false);
4391 bool do_deselect = true;
4392 MidiTimeAxisView* mtv;
4394 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4396 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4397 /* nothing selected */
4398 add_midi_region (mtv);
4399 do_deselect = false;
4403 /* do not deselect if Primary or Tertiary (toggle-select or
4404 * extend-select are pressed.
4407 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4408 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4415 _editor->rubberband_rect->hide();
4419 RubberbandSelectDrag::aborted (bool)
4421 _editor->rubberband_rect->hide ();
4424 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4425 : RegionDrag (e, i, p, v)
4427 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4431 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4433 Drag::start_grab (event, cursor);
4435 show_verbose_cursor_time (adjusted_current_frame (event));
4439 TimeFXDrag::motion (GdkEvent* event, bool)
4441 RegionView* rv = _primary;
4442 StreamView* cv = rv->get_time_axis_view().view ();
4444 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4445 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4446 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4448 framepos_t const pf = adjusted_current_frame (event);
4450 if (pf > rv->region()->position()) {
4451 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4454 show_verbose_cursor_time (pf);
4458 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4460 _primary->get_time_axis_view().hide_timestretch ();
4462 if (!movement_occurred) {
4466 if (last_pointer_frame() < _primary->region()->position()) {
4467 /* backwards drag of the left edge - not usable */
4471 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4473 float percentage = (double) newlen / (double) _primary->region()->length();
4475 #ifndef USE_RUBBERBAND
4476 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4477 if (_primary->region()->data_type() == DataType::AUDIO) {
4478 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4482 if (!_editor->get_selection().regions.empty()) {
4483 /* primary will already be included in the selection, and edit
4484 group shared editing will propagate selection across
4485 equivalent regions, so just use the current region
4489 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4490 error << _("An error occurred while executing time stretch operation") << endmsg;
4496 TimeFXDrag::aborted (bool)
4498 _primary->get_time_axis_view().hide_timestretch ();
4501 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4504 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4508 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4510 Drag::start_grab (event);
4514 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4516 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4520 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4522 if (movement_occurred && _editor->session()) {
4523 /* make sure we stop */
4524 _editor->session()->request_transport_speed (0.0);
4529 ScrubDrag::aborted (bool)
4534 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4538 , _time_selection_at_start (!_editor->get_selection().time.empty())
4540 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4542 if (_time_selection_at_start) {
4543 start_at_start = _editor->get_selection().time.start();
4544 end_at_start = _editor->get_selection().time.end_frame();
4549 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4551 if (_editor->session() == 0) {
4555 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4557 switch (_operation) {
4558 case CreateSelection:
4559 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4564 cursor = _editor->cursors()->selector;
4565 Drag::start_grab (event, cursor);
4568 case SelectionStartTrim:
4569 if (_editor->clicked_axisview) {
4570 _editor->clicked_axisview->order_selection_trims (_item, true);
4572 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4575 case SelectionEndTrim:
4576 if (_editor->clicked_axisview) {
4577 _editor->clicked_axisview->order_selection_trims (_item, false);
4579 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4583 Drag::start_grab (event, cursor);
4586 case SelectionExtend:
4587 Drag::start_grab (event, cursor);
4591 if (_operation == SelectionMove) {
4592 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4594 show_verbose_cursor_time (adjusted_current_frame (event));
4599 SelectionDrag::setup_pointer_frame_offset ()
4601 switch (_operation) {
4602 case CreateSelection:
4603 _pointer_frame_offset = 0;
4606 case SelectionStartTrim:
4608 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4611 case SelectionEndTrim:
4612 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4615 case SelectionExtend:
4621 SelectionDrag::motion (GdkEvent* event, bool first_move)
4623 framepos_t start = 0;
4625 framecnt_t length = 0;
4626 framecnt_t distance = 0;
4628 framepos_t const pending_position = adjusted_current_frame (event);
4630 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4634 switch (_operation) {
4635 case CreateSelection:
4637 framepos_t grab = grab_frame ();
4640 grab = adjusted_current_frame (event, false);
4641 if (grab < pending_position) {
4642 _editor->snap_to (grab, RoundDownMaybe);
4644 _editor->snap_to (grab, RoundUpMaybe);
4648 if (pending_position < grab) {
4649 start = pending_position;
4652 end = pending_position;
4656 /* first drag: Either add to the selection
4657 or create a new selection
4664 /* adding to the selection */
4665 _editor->set_selected_track_as_side_effect (Selection::Add);
4666 _editor->clicked_selection = _editor->selection->add (start, end);
4673 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4674 _editor->set_selected_track_as_side_effect (Selection::Set);
4677 _editor->clicked_selection = _editor->selection->set (start, end);
4681 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4682 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4683 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4685 _editor->selection->add (atest);
4689 /* select all tracks within the rectangle that we've marked out so far */
4690 TrackViewList new_selection;
4691 TrackViewList& all_tracks (_editor->track_views);
4693 ArdourCanvas::Coord const top = grab_y();
4694 ArdourCanvas::Coord const bottom = current_pointer_y();
4696 if (top >= 0 && bottom >= 0) {
4698 //first, find the tracks that are covered in the y range selection
4699 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4700 if ((*i)->covered_by_y_range (top, bottom)) {
4701 new_selection.push_back (*i);
4705 //now find any tracks that are GROUPED with the tracks we selected
4706 TrackViewList grouped_add = new_selection;
4707 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4708 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4709 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4710 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4711 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4712 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4713 grouped_add.push_back (*j);
4718 //now compare our list with the current selection, and add or remove as necessary
4719 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4720 TrackViewList tracks_to_add;
4721 TrackViewList tracks_to_remove;
4722 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4723 if ( !_editor->selection->tracks.contains ( *i ) )
4724 tracks_to_add.push_back ( *i );
4725 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4726 if ( !grouped_add.contains ( *i ) )
4727 tracks_to_remove.push_back ( *i );
4728 _editor->selection->add(tracks_to_add);
4729 _editor->selection->remove(tracks_to_remove);
4735 case SelectionStartTrim:
4737 start = _editor->selection->time[_editor->clicked_selection].start;
4738 end = _editor->selection->time[_editor->clicked_selection].end;
4740 if (pending_position > end) {
4743 start = pending_position;
4747 case SelectionEndTrim:
4749 start = _editor->selection->time[_editor->clicked_selection].start;
4750 end = _editor->selection->time[_editor->clicked_selection].end;
4752 if (pending_position < start) {
4755 end = pending_position;
4762 start = _editor->selection->time[_editor->clicked_selection].start;
4763 end = _editor->selection->time[_editor->clicked_selection].end;
4765 length = end - start;
4766 distance = pending_position - start;
4767 start = pending_position;
4768 _editor->snap_to (start);
4770 end = start + length;
4774 case SelectionExtend:
4779 switch (_operation) {
4781 if (_time_selection_at_start) {
4782 _editor->selection->move_time (distance);
4786 _editor->selection->replace (_editor->clicked_selection, start, end);
4790 if (_operation == SelectionMove) {
4791 show_verbose_cursor_time(start);
4793 show_verbose_cursor_time(pending_position);
4798 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4800 Session* s = _editor->session();
4802 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4803 if (movement_occurred) {
4804 motion (event, false);
4805 /* XXX this is not object-oriented programming at all. ick */
4806 if (_editor->selection->time.consolidate()) {
4807 _editor->selection->TimeChanged ();
4810 /* XXX what if its a music time selection? */
4812 if ( s->get_play_range() && s->transport_rolling() ) {
4813 s->request_play_range (&_editor->selection->time, true);
4815 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4816 if (_operation == SelectionEndTrim)
4817 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4819 s->request_locate (_editor->get_selection().time.start());
4825 /* just a click, no pointer movement.
4828 if (_operation == SelectionExtend) {
4829 if (_time_selection_at_start) {
4830 framepos_t pos = adjusted_current_frame (event, false);
4831 framepos_t start = min (pos, start_at_start);
4832 framepos_t end = max (pos, end_at_start);
4833 _editor->selection->set (start, end);
4836 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4837 if (_editor->clicked_selection) {
4838 _editor->selection->remove (_editor->clicked_selection);
4841 if (!_editor->clicked_selection) {
4842 _editor->selection->clear_time();
4847 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4848 _editor->selection->set (_editor->clicked_axisview);
4851 if (s && s->get_play_range () && s->transport_rolling()) {
4852 s->request_stop (false, false);
4857 _editor->stop_canvas_autoscroll ();
4858 _editor->clicked_selection = 0;
4859 _editor->commit_reversible_selection_op ();
4863 SelectionDrag::aborted (bool)
4868 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4869 : Drag (e, i, false),
4873 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4875 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4876 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4877 physical_screen_height (_editor->get_window())));
4878 _drag_rect->hide ();
4880 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4881 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4884 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4886 /* normal canvas items will be cleaned up when their parent group is deleted. But
4887 this item is created as the child of a long-lived parent group, and so we
4888 need to explicitly delete it.
4894 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4896 if (_editor->session() == 0) {
4900 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4902 if (!_editor->temp_location) {
4903 _editor->temp_location = new Location (*_editor->session());
4906 switch (_operation) {
4907 case CreateSkipMarker:
4908 case CreateRangeMarker:
4909 case CreateTransportMarker:
4910 case CreateCDMarker:
4912 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4917 cursor = _editor->cursors()->selector;
4921 Drag::start_grab (event, cursor);
4923 show_verbose_cursor_time (adjusted_current_frame (event));
4927 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4929 framepos_t start = 0;
4931 ArdourCanvas::Rectangle *crect;
4933 switch (_operation) {
4934 case CreateSkipMarker:
4935 crect = _editor->range_bar_drag_rect;
4937 case CreateRangeMarker:
4938 crect = _editor->range_bar_drag_rect;
4940 case CreateTransportMarker:
4941 crect = _editor->transport_bar_drag_rect;
4943 case CreateCDMarker:
4944 crect = _editor->cd_marker_bar_drag_rect;
4947 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4952 framepos_t const pf = adjusted_current_frame (event);
4954 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4955 framepos_t grab = grab_frame ();
4956 _editor->snap_to (grab);
4958 if (pf < grab_frame()) {
4966 /* first drag: Either add to the selection
4967 or create a new selection.
4972 _editor->temp_location->set (start, end);
4976 update_item (_editor->temp_location);
4978 //_drag_rect->raise_to_top();
4984 _editor->temp_location->set (start, end);
4986 double x1 = _editor->sample_to_pixel (start);
4987 double x2 = _editor->sample_to_pixel (end);
4991 update_item (_editor->temp_location);
4994 show_verbose_cursor_time (pf);
4999 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5001 Location * newloc = 0;
5005 if (movement_occurred) {
5006 motion (event, false);
5009 switch (_operation) {
5010 case CreateSkipMarker:
5011 case CreateRangeMarker:
5012 case CreateCDMarker:
5014 XMLNode &before = _editor->session()->locations()->get_state();
5015 if (_operation == CreateSkipMarker) {
5016 _editor->begin_reversible_command (_("new skip marker"));
5017 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5018 flags = Location::IsRangeMarker | Location::IsSkip;
5019 _editor->range_bar_drag_rect->hide();
5020 } else if (_operation == CreateCDMarker) {
5021 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5022 _editor->begin_reversible_command (_("new CD marker"));
5023 flags = Location::IsRangeMarker | Location::IsCDMarker;
5024 _editor->cd_marker_bar_drag_rect->hide();
5026 _editor->begin_reversible_command (_("new skip marker"));
5027 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5028 flags = Location::IsRangeMarker;
5029 _editor->range_bar_drag_rect->hide();
5031 newloc = new Location (
5032 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5035 _editor->session()->locations()->add (newloc, true);
5036 XMLNode &after = _editor->session()->locations()->get_state();
5037 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5038 _editor->commit_reversible_command ();
5042 case CreateTransportMarker:
5043 // popup menu to pick loop or punch
5044 _editor->new_transport_marker_context_menu (&event->button, _item);
5050 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5052 if (_operation == CreateTransportMarker) {
5054 /* didn't drag, so just locate */
5056 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5058 } else if (_operation == CreateCDMarker) {
5060 /* didn't drag, but mark is already created so do
5063 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5068 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5070 if (end == max_framepos) {
5071 end = _editor->session()->current_end_frame ();
5074 if (start == max_framepos) {
5075 start = _editor->session()->current_start_frame ();
5078 switch (_editor->mouse_mode) {
5080 /* find the two markers on either side and then make the selection from it */
5081 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5085 /* find the two markers on either side of the click and make the range out of it */
5086 _editor->selection->set (start, end);
5095 _editor->stop_canvas_autoscroll ();
5099 RangeMarkerBarDrag::aborted (bool movement_occured)
5101 if (movement_occured) {
5102 _drag_rect->hide ();
5107 RangeMarkerBarDrag::update_item (Location* location)
5109 double const x1 = _editor->sample_to_pixel (location->start());
5110 double const x2 = _editor->sample_to_pixel (location->end());
5112 _drag_rect->set_x0 (x1);
5113 _drag_rect->set_x1 (x2);
5116 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5118 , _cumulative_dx (0)
5119 , _cumulative_dy (0)
5121 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5123 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5125 _region = &_primary->region_view ();
5126 _note_height = _region->midi_stream_view()->note_height ();
5130 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5132 Drag::start_grab (event);
5134 if (!(_was_selected = _primary->selected())) {
5136 /* tertiary-click means extend selection - we'll do that on button release,
5137 so don't add it here, because otherwise we make it hard to figure
5138 out the "extend-to" range.
5141 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5144 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5147 _region->note_selected (_primary, true);
5149 _region->unique_select (_primary);
5152 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5153 _editor->commit_reversible_selection_op();
5158 /** @return Current total drag x change in frames */
5160 NoteDrag::total_dx () const
5163 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5165 /* primary note time */
5166 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5168 /* new time of the primary note in session frames */
5169 frameoffset_t st = n + dx;
5171 framepos_t const rp = _region->region()->position ();
5173 /* prevent the note being dragged earlier than the region's position */
5176 /* snap and return corresponding delta */
5177 return _region->snap_frame_to_frame (st - rp) + rp - n;
5180 /** @return Current total drag y change in note number */
5182 NoteDrag::total_dy () const
5184 MidiStreamView* msv = _region->midi_stream_view ();
5185 double const y = _region->midi_view()->y_position ();
5186 /* new current note */
5187 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5189 n = max (msv->lowest_note(), n);
5190 n = min (msv->highest_note(), n);
5191 /* and work out delta */
5192 return n - msv->y_to_note (grab_y() - y);
5196 NoteDrag::motion (GdkEvent *, bool)
5198 /* Total change in x and y since the start of the drag */
5199 frameoffset_t const dx = total_dx ();
5200 int8_t const dy = total_dy ();
5202 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5203 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5204 double const tdy = -dy * _note_height - _cumulative_dy;
5207 _cumulative_dx += tdx;
5208 _cumulative_dy += tdy;
5210 int8_t note_delta = total_dy();
5212 _region->move_selection (tdx, tdy, note_delta);
5214 /* the new note value may be the same as the old one, but we
5215 * don't know what that means because the selection may have
5216 * involved more than one note and we might be doing something
5217 * odd with them. so show the note value anyway, always.
5221 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5223 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5224 (int) floor ((double)new_note));
5226 show_verbose_cursor_text (buf);
5231 NoteDrag::finished (GdkEvent* ev, bool moved)
5234 /* no motion - select note */
5236 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5237 _editor->current_mouse_mode() == Editing::MouseDraw) {
5239 bool changed = false;
5241 if (_was_selected) {
5242 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5244 _region->note_deselected (_primary);
5248 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5249 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5251 if (!extend && !add && _region->selection_size() > 1) {
5252 _region->unique_select (_primary);
5254 } else if (extend) {
5255 _region->note_selected (_primary, true, true);
5258 /* it was added during button press */
5263 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5264 _editor->commit_reversible_selection_op();
5268 _region->note_dropped (_primary, total_dx(), total_dy());
5273 NoteDrag::aborted (bool)
5278 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5279 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5280 : Drag (editor, atv->base_item ())
5282 , _y_origin (atv->y_position())
5283 , _nothing_to_drag (false)
5285 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5286 setup (atv->lines ());
5289 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5290 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5291 : Drag (editor, rv->get_canvas_group ())
5293 , _y_origin (rv->get_time_axis_view().y_position())
5294 , _nothing_to_drag (false)
5297 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5299 list<boost::shared_ptr<AutomationLine> > lines;
5301 AudioRegionView* audio_view;
5302 AutomationRegionView* automation_view;
5303 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5304 lines.push_back (audio_view->get_gain_line ());
5305 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5306 lines.push_back (automation_view->line ());
5309 error << _("Automation range drag created for invalid region type") << endmsg;
5315 /** @param lines AutomationLines to drag.
5316 * @param offset Offset from the session start to the points in the AutomationLines.
5319 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5321 /* find the lines that overlap the ranges being dragged */
5322 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5323 while (i != lines.end ()) {
5324 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5327 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5329 /* check this range against all the AudioRanges that we are using */
5330 list<AudioRange>::const_iterator k = _ranges.begin ();
5331 while (k != _ranges.end()) {
5332 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5338 /* add it to our list if it overlaps at all */
5339 if (k != _ranges.end()) {
5344 _lines.push_back (n);
5350 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5354 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5356 return 1.0 - ((global_y - _y_origin) / line->height());
5360 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5362 const double v = list->eval(x);
5363 return _integral ? rint(v) : v;
5367 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5369 Drag::start_grab (event, cursor);
5371 /* Get line states before we start changing things */
5372 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5373 i->state = &i->line->get_state ();
5374 i->original_fraction = y_fraction (i->line, current_pointer_y());
5377 if (_ranges.empty()) {
5379 /* No selected time ranges: drag all points */
5380 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5381 uint32_t const N = i->line->npoints ();
5382 for (uint32_t j = 0; j < N; ++j) {
5383 i->points.push_back (i->line->nth (j));
5389 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5391 framecnt_t const half = (i->start + i->end) / 2;
5393 /* find the line that this audio range starts in */
5394 list<Line>::iterator j = _lines.begin();
5395 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5399 if (j != _lines.end()) {
5400 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5402 /* j is the line that this audio range starts in; fade into it;
5403 64 samples length plucked out of thin air.
5406 framepos_t a = i->start + 64;
5411 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5412 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5414 the_list->editor_add (p, value (the_list, p));
5415 the_list->editor_add (q, value (the_list, q));
5418 /* same thing for the end */
5421 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5425 if (j != _lines.end()) {
5426 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5428 /* j is the line that this audio range starts in; fade out of it;
5429 64 samples length plucked out of thin air.
5432 framepos_t b = i->end - 64;
5437 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5438 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5440 the_list->editor_add (p, value (the_list, p));
5441 the_list->editor_add (q, value (the_list, q));
5445 _nothing_to_drag = true;
5447 /* Find all the points that should be dragged and put them in the relevant
5448 points lists in the Line structs.
5451 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5453 uint32_t const N = i->line->npoints ();
5454 for (uint32_t j = 0; j < N; ++j) {
5456 /* here's a control point on this line */
5457 ControlPoint* p = i->line->nth (j);
5458 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5460 /* see if it's inside a range */
5461 list<AudioRange>::const_iterator k = _ranges.begin ();
5462 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5466 if (k != _ranges.end()) {
5467 /* dragging this point */
5468 _nothing_to_drag = false;
5469 i->points.push_back (p);
5475 if (_nothing_to_drag) {
5479 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5480 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5485 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5487 if (_nothing_to_drag) {
5491 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5492 float const f = y_fraction (l->line, current_pointer_y());
5493 /* we are ignoring x position for this drag, so we can just pass in anything */
5495 l->line->drag_motion (0, f, true, false, ignored);
5496 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5501 AutomationRangeDrag::finished (GdkEvent* event, bool)
5503 if (_nothing_to_drag) {
5507 motion (event, false);
5508 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5509 i->line->end_drag (false, 0);
5512 _editor->commit_reversible_command ();
5516 AutomationRangeDrag::aborted (bool)
5518 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5523 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5525 , initial_time_axis_view (itav)
5527 /* note that time_axis_view may be null if the regionview was created
5528 * as part of a copy operation.
5530 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5531 layer = v->region()->layer ();
5532 initial_y = v->get_canvas_group()->position().y;
5533 initial_playlist = v->region()->playlist ();
5534 initial_position = v->region()->position ();
5535 initial_end = v->region()->position () + v->region()->length ();
5538 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5539 : Drag (e, i->canvas_item ())
5542 , _cumulative_dx (0)
5544 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5545 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5550 PatchChangeDrag::motion (GdkEvent* ev, bool)
5552 framepos_t f = adjusted_current_frame (ev);
5553 boost::shared_ptr<Region> r = _region_view->region ();
5554 f = max (f, r->position ());
5555 f = min (f, r->last_frame ());
5557 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5558 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5559 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5560 _cumulative_dx = dxu;
5564 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5566 if (!movement_occurred) {
5570 boost::shared_ptr<Region> r (_region_view->region ());
5571 framepos_t f = adjusted_current_frame (ev);
5572 f = max (f, r->position ());
5573 f = min (f, r->last_frame ());
5575 _region_view->move_patch_change (
5577 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5582 PatchChangeDrag::aborted (bool)
5584 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5588 PatchChangeDrag::setup_pointer_frame_offset ()
5590 boost::shared_ptr<Region> region = _region_view->region ();
5591 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5594 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5595 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5602 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5604 _region_view->update_drag_selection (
5606 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5610 MidiRubberbandSelectDrag::deselect_things ()
5615 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5616 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5619 _vertical_only = true;
5623 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5625 double const y = _region_view->midi_view()->y_position ();
5627 y1 = max (0.0, y1 - y);
5628 y2 = max (0.0, y2 - y);
5630 _region_view->update_vertical_drag_selection (
5633 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5638 MidiVerticalSelectDrag::deselect_things ()
5643 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5644 : RubberbandSelectDrag (e, i)
5650 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5652 if (drag_in_progress) {
5653 /* We just want to select things at the end of the drag, not during it */
5657 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5659 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5661 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5663 _editor->commit_reversible_selection_op ();
5667 EditorRubberbandSelectDrag::deselect_things ()
5669 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5671 _editor->selection->clear_tracks();
5672 _editor->selection->clear_regions();
5673 _editor->selection->clear_points ();
5674 _editor->selection->clear_lines ();
5675 _editor->selection->clear_midi_notes ();
5677 _editor->commit_reversible_selection_op();
5680 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5685 _note[0] = _note[1] = 0;
5688 NoteCreateDrag::~NoteCreateDrag ()
5694 NoteCreateDrag::grid_frames (framepos_t t) const
5697 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5699 grid_beats = Evoral::Beats(1);
5702 return _region_view->region_beats_to_region_frames (grid_beats);
5706 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5708 Drag::start_grab (event, cursor);
5710 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5712 framepos_t pf = _drags->current_pointer_frame ();
5713 framecnt_t const g = grid_frames (pf);
5715 /* Hack so that we always snap to the note that we are over, instead of snapping
5716 to the next one if we're more than halfway through the one we're over.
5718 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5722 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5723 _note[1] = _note[0];
5725 MidiStreamView* sv = _region_view->midi_stream_view ();
5726 double const x = _editor->sample_to_pixel (_note[0]);
5727 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5729 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5730 _drag_rect->set_outline_all ();
5731 _drag_rect->set_outline_color (0xffffff99);
5732 _drag_rect->set_fill_color (0xffffff66);
5736 NoteCreateDrag::motion (GdkEvent* event, bool)
5738 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5739 double const x0 = _editor->sample_to_pixel (_note[0]);
5740 double const x1 = _editor->sample_to_pixel (_note[1]);
5741 _drag_rect->set_x0 (std::min(x0, x1));
5742 _drag_rect->set_x1 (std::max(x0, x1));
5746 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5748 if (!had_movement) {
5752 framepos_t const start = min (_note[0], _note[1]);
5753 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5755 framecnt_t const g = grid_frames (start);
5756 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5758 if (_editor->snap_mode() == SnapNormal && length < g) {
5762 Evoral::Beats length_beats = max (
5763 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5765 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5769 NoteCreateDrag::y_to_region (double y) const
5772 _region_view->get_canvas_group()->canvas_to_item (x, y);
5777 NoteCreateDrag::aborted (bool)
5782 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5787 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5791 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5793 Drag::start_grab (event, cursor);
5797 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5803 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5806 distance = _drags->current_pointer_x() - grab_x();
5807 len = ar->fade_in()->back()->when;
5809 distance = grab_x() - _drags->current_pointer_x();
5810 len = ar->fade_out()->back()->when;
5813 /* how long should it be ? */
5815 new_length = len + _editor->pixel_to_sample (distance);
5817 /* now check with the region that this is legal */
5819 new_length = ar->verify_xfade_bounds (new_length, start);
5822 arv->reset_fade_in_shape_width (ar, new_length);
5824 arv->reset_fade_out_shape_width (ar, new_length);
5829 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5835 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5838 distance = _drags->current_pointer_x() - grab_x();
5839 len = ar->fade_in()->back()->when;
5841 distance = grab_x() - _drags->current_pointer_x();
5842 len = ar->fade_out()->back()->when;
5845 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5847 _editor->begin_reversible_command ("xfade trim");
5848 ar->playlist()->clear_owned_changes ();
5851 ar->set_fade_in_length (new_length);
5853 ar->set_fade_out_length (new_length);
5856 /* Adjusting the xfade may affect other regions in the playlist, so we need
5857 to get undo Commands from the whole playlist rather than just the
5861 vector<Command*> cmds;
5862 ar->playlist()->rdiff (cmds);
5863 _editor->session()->add_commands (cmds);
5864 _editor->commit_reversible_command ();
5869 CrossfadeEdgeDrag::aborted (bool)
5872 // arv->redraw_start_xfade ();
5874 // arv->redraw_end_xfade ();
5878 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5879 : Drag (e, item, true)
5880 , line (new EditorCursor (*e))
5882 line->set_position (pos);
5886 RegionCutDrag::~RegionCutDrag ()
5892 RegionCutDrag::motion (GdkEvent*, bool)
5894 framepos_t where = _drags->current_pointer_frame();
5895 _editor->snap_to (where);
5897 line->set_position (where);
5901 RegionCutDrag::finished (GdkEvent*, bool)
5903 _editor->get_track_canvas()->canvas()->re_enter();
5905 framepos_t pos = _drags->current_pointer_frame();
5909 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5915 _editor->split_regions_at (pos, rs);
5919 RegionCutDrag::aborted (bool)