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 dt = delta > 0 ? +1 : -1;
656 int target = start + delta - skip;
658 assert (current < 0 || current >= _time_axis_views.size() || !_time_axis_views[current]->hidden());
659 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
661 while (current >= 0 && current != target) {
663 if (current < 0 && dt < 0) {
666 if (current >= _time_axis_views.size() && dt > 0) {
669 if (current < 0 || current >= _time_axis_views.size()) {
673 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
674 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
678 if (distance_only && current == start + delta) {
686 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
688 if (_y_constrained) {
692 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
693 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
694 assert (n < 0 || n >= _time_axis_views.size() || !_time_axis_views[n]->hidden());
696 if (i->time_axis_view < 0 || i->time_axis_view >= _time_axis_views.size()) {
697 /* already in the drop zone */
698 if (delta_track >= 0) {
699 /* downward motion - OK if others are still not in the dropzone */
708 } else if (n >= int (_time_axis_views.size())) {
709 /* downward motion into drop zone. That's fine. */
713 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
714 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
715 /* not a track, or the wrong type */
719 double const l = i->layer + delta_layer;
721 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
722 mode to allow the user to place a region below another on layer 0.
724 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
725 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
726 If it has, the layers will be munged later anyway, so it's ok.
732 /* all regions being dragged are ok with this change */
736 struct DraggingViewSorter {
737 bool operator() (const DraggingView& a, const DraggingView& b) {
738 return a.time_axis_view < b.time_axis_view;
743 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
745 double delta_layer = 0;
746 int delta_time_axis_view = 0;
747 int current_pointer_time_axis_view = -1;
749 assert (!_views.empty ());
753 if (initially_vertical()) {
754 _y_constrained = false;
755 _x_constrained = true;
757 _y_constrained = true;
758 _x_constrained = false;
763 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
765 /* Find the TimeAxisView that the pointer is now over */
766 const double cur_y = current_pointer_y ();
767 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
768 TimeAxisView* tv = r.first;
770 if (!tv && cur_y < 0) {
771 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
775 /* find drop-zone y-position */
776 Coord last_track_bottom_edge;
777 last_track_bottom_edge = 0;
778 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
779 if (!(*t)->hidden()) {
780 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
785 if (tv && tv->view()) {
786 /* the mouse is over a track */
787 double layer = r.second;
789 if (first_move && tv->view()->layer_display() == Stacked) {
790 tv->view()->set_layer_display (Expanded);
793 /* Here's the current pointer position in terms of time axis view and layer */
794 current_pointer_time_axis_view = find_time_axis_view (tv);
795 assert(current_pointer_time_axis_view >= 0);
797 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
799 /* Work out the change in y */
801 if (_last_pointer_time_axis_view < 0) {
802 /* Was in the drop-zone, now over a track.
803 * Hence it must be an upward move (from the bottom)
805 * track_index is still -1, so delta must be set to
806 * move up the correct number of tracks from the bottom.
808 * This is necessary because steps may be skipped if
809 * the bottom-most track is not a valid target and/or
810 * if there are hidden tracks at the bottom.
811 * Hence the initial offset (_ddropzone) as well as the
812 * last valid pointer position (_pdropzone) need to be
813 * taken into account.
815 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
817 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
820 /* TODO needs adjustment per DraggingView,
822 * e.g. select one region on the top-layer of a track
823 * and one region which is at the bottom-layer of another track
826 * Indicated drop-zones and layering is wrong.
827 * and may infer additional layers on the target-track
828 * (depending how many layers the original track had).
830 * Or select two regions (different layers) on a same track,
831 * move across a non-layer track.. -> layering info is lost.
832 * on drop either of the regions may be on top.
834 * Proposed solution: screw it :) well,
835 * don't use delta_layer, use an absolute value
836 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
837 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
838 * 3) iterate over all DraggingView, find the one that is over the track with most layers
839 * 4) proportionally scale layer to layers available on target
841 delta_layer = current_pointer_layer - _last_pointer_layer;
844 /* for automation lanes, there is a TimeAxisView but no ->view()
845 * if (!tv) -> dropzone
847 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
848 /* Moving into the drop-zone.. */
849 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
850 /* delta_time_axis_view may not be sufficient to move into the DZ
851 * the mouse may enter it, but it may not be a valid move due to
854 * -> remember the delta needed to move into the dropzone
856 _ddropzone = delta_time_axis_view;
857 /* ..but subtract hidden tracks (or routes) at the bottom.
858 * we silently move mover them
860 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
861 - _time_axis_views.size();
863 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
864 /* move around inside the zone.
865 * This allows to move further down until all regions are in the zone.
867 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
868 assert(ptr_y >= last_track_bottom_edge);
869 assert(_ddropzone > 0);
871 /* calculate mouse position in 'tracks' below last track. */
872 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
873 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
875 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
877 delta_time_axis_view = dzpos - _pdropzone;
878 } else if (dzpos < _pdropzone && _ndropzone > 0) {
879 // move up inside the DZ
880 delta_time_axis_view = dzpos - _pdropzone;
884 /* Work out the change in x */
885 framepos_t pending_region_position;
886 double const x_delta = compute_x_delta (event, &pending_region_position);
887 _last_frame_position = pending_region_position;
889 /* calculate hidden tracks in current y-axis delta */
891 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
892 /* The mouse is more than one track below the dropzone.
893 * distance calculation is not needed (and would not work, either
894 * because the dropzone is "packed").
896 * Except when partially(!) moving regions out of dropzone in a large step.
897 * (the mouse may or may not remain in the DZ)
898 * Hidden tracks at the bottom of the TAV need to be skipped.
900 assert(_pdropzone >= _ddropzone);
901 if (delta_time_axis_view < 0 && -delta_time_axis_view >= _pdropzone - _ddropzone)
903 const int dt = delta_time_axis_view + _pdropzone - _ddropzone;
905 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
906 -_time_axis_views.size() - dt;
909 else if (_last_pointer_time_axis_view < 0) {
910 /* Moving out of the zone. Check for hidden tracks at the bottom. */
911 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
912 -_time_axis_views.size() - delta_time_axis_view;
914 /* calculate hidden tracks that are skipped by the pointer movement */
915 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
916 - _last_pointer_time_axis_view
917 - delta_time_axis_view;
920 /* Verify change in y */
921 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
922 /* this y movement is not allowed, so do no y movement this time */
923 delta_time_axis_view = 0;
928 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
929 /* haven't reached next snap point, and we're not switching
930 trackviews nor layers. nothing to do.
935 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
936 PlaylistDropzoneMap playlist_dropzone_map;
937 _ndropzone = 0; // number of elements currently in the dropzone
940 /* sort views by time_axis.
941 * This retains track order in the dropzone, regardless
942 * of actual selection order
944 _views.sort (DraggingViewSorter());
946 /* count number of distinct tracks of all regions
947 * being dragged, used for dropzone.
950 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
951 if (i->time_axis_view != prev_track) {
952 prev_track = i->time_axis_view;
958 _views.back().time_axis_view -
959 _views.front().time_axis_view;
961 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
962 - _views.back().time_axis_view;
964 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
968 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
970 RegionView* rv = i->view;
975 if (rv->region()->locked() || rv->region()->video_locked()) {
982 /* reparent the regionview into a group above all
986 ArdourCanvas::Item* rvg = rv->get_canvas_group();
987 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
988 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
989 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
990 /* move the item so that it continues to appear at the
991 same location now that its parent has changed.
993 rvg->move (rv_canvas_offset - dmg_canvas_offset);
996 /* If we have moved tracks, we'll fudge the layer delta so that the
997 region gets moved back onto layer 0 on its new track; this avoids
998 confusion when dragging regions from non-zero layers onto different
1001 double this_delta_layer = delta_layer;
1002 if (delta_time_axis_view != 0) {
1003 this_delta_layer = - i->layer;
1006 int this_delta_time_axis_view = delta_time_axis_view;
1007 this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1009 int track_index = i->time_axis_view + this_delta_time_axis_view;
1010 assert(track_index >= 0);
1012 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1013 /* Track is in the Dropzone */
1015 i->time_axis_view = track_index;
1016 assert(i->time_axis_view >= _time_axis_views.size());
1019 double yposition = 0;
1020 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1021 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1024 /* store index of each new playlist as a negative count, starting at -1 */
1026 if (pdz == playlist_dropzone_map.end()) {
1028 int n = playlist_dropzone_map.size() + 1;
1030 /* compute where this new track (which doesn't exist yet) will live
1034 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1036 /* How high is this region view ? */
1038 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1039 ArdourCanvas::Rect bbox;
1042 bbox = obbox.get ();
1045 last_track_bottom_edge += bbox.height();
1047 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1050 yposition = pdz->second;
1053 /* values are zero or negative, hence the use of min() */
1054 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1059 /* The TimeAxisView that this region is now over */
1060 TimeAxisView* current_tv = _time_axis_views[track_index];
1062 /* Ensure it is moved from stacked -> expanded if appropriate */
1063 if (current_tv->view()->layer_display() == Stacked) {
1064 current_tv->view()->set_layer_display (Expanded);
1067 /* We're only allowed to go -ve in layer on Expanded views */
1068 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1069 this_delta_layer = - i->layer;
1073 rv->set_height (current_tv->view()->child_height ());
1075 /* Update show/hidden status as the region view may have come from a hidden track,
1076 or have moved to one.
1078 if (current_tv->hidden ()) {
1079 rv->get_canvas_group()->hide ();
1081 rv->get_canvas_group()->show ();
1084 /* Update the DraggingView */
1085 i->time_axis_view = track_index;
1086 i->layer += this_delta_layer;
1089 _editor->mouse_brush_insert_region (rv, pending_region_position);
1093 /* Get the y coordinate of the top of the track that this region is now over */
1094 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1096 /* And adjust for the layer that it should be on */
1097 StreamView* cv = current_tv->view ();
1098 switch (cv->layer_display ()) {
1102 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1105 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1109 /* need to get the parent of the regionview
1110 * canvas group and get its position in
1111 * equivalent coordinate space as the trackview
1112 * we are now dragging over.
1115 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1120 /* Now move the region view */
1121 rv->move (x_delta, y_delta);
1123 } /* foreach region */
1125 _total_x_delta += x_delta;
1127 if (x_delta != 0 && !_brushing) {
1128 show_verbose_cursor_time (_last_frame_position);
1131 /* keep track of pointer movement */
1133 /* the pointer is currently over a time axis view */
1135 if (_last_pointer_time_axis_view < 0) {
1136 /* last motion event was not over a time axis view
1137 * or last y-movement out of the dropzone was not valid
1140 if (delta_time_axis_view < 0) {
1141 /* in the drop zone, moving up */
1143 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1144 * We do not use negative _last_pointer_time_axis_view because
1145 * the dropzone is "packed" (the actual track offset is ignored)
1147 * As opposed to the actual number
1148 * of elements in the dropzone (_ndropzone)
1149 * _pdropzone is not constrained. This is necessary
1150 * to allow moving multiple regions with y-distance
1153 * There can be 0 elements in the dropzone,
1154 * even though the drag-pointer is inside the DZ.
1157 * [ Audio-track, Midi-track, Audio-track, DZ ]
1158 * move regions from both audio tracks at the same time into the
1159 * DZ by grabbing the region in the bottom track.
1161 assert(current_pointer_time_axis_view >= 0);
1162 dtz = std::min((int)_pdropzone, -delta_time_axis_view);
1166 /* only move out of the zone if the movement is OK */
1167 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1168 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1171 /* last motion event was also over a time axis view */
1172 _last_pointer_time_axis_view += delta_time_axis_view;
1173 assert(_last_pointer_time_axis_view >= 0);
1178 /* the pointer is not over a time axis view */
1179 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1180 _pdropzone += delta_time_axis_view - delta_skip;
1181 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1184 _last_pointer_layer += delta_layer;
1188 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1190 if (_copy && first_move) {
1192 if (_x_constrained) {
1193 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1195 _editor->begin_reversible_command (Operations::region_copy);
1198 /* duplicate the regionview(s) and region(s) */
1200 list<DraggingView> new_regionviews;
1202 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1204 RegionView* rv = i->view;
1205 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1206 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1208 const boost::shared_ptr<const Region> original = rv->region();
1209 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1210 region_copy->set_position (original->position());
1211 /* need to set this so that the drop zone code can work. This doesn't
1212 actually put the region into the playlist, but just sets a weak pointer
1215 region_copy->set_playlist (original->playlist());
1219 boost::shared_ptr<AudioRegion> audioregion_copy
1220 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1222 nrv = new AudioRegionView (*arv, audioregion_copy);
1224 boost::shared_ptr<MidiRegion> midiregion_copy
1225 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1226 nrv = new MidiRegionView (*mrv, midiregion_copy);
1231 nrv->get_canvas_group()->show ();
1232 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1234 /* swap _primary to the copy */
1236 if (rv == _primary) {
1240 /* ..and deselect the one we copied */
1242 rv->set_selected (false);
1245 if (!new_regionviews.empty()) {
1247 /* reflect the fact that we are dragging the copies */
1249 _views = new_regionviews;
1251 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1254 } else if (!_copy && first_move) {
1256 if (_x_constrained) {
1257 _editor->begin_reversible_command (_("fixed time region drag"));
1259 _editor->begin_reversible_command (Operations::region_drag);
1263 RegionMotionDrag::motion (event, first_move);
1267 RegionMotionDrag::finished (GdkEvent *, bool)
1269 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1270 if (!(*i)->view()) {
1274 if ((*i)->view()->layer_display() == Expanded) {
1275 (*i)->view()->set_layer_display (Stacked);
1281 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1283 RegionMotionDrag::finished (ev, movement_occurred);
1285 if (!movement_occurred) {
1289 if (was_double_click() && !_views.empty()) {
1290 DraggingView dv = _views.front();
1291 dv.view->show_region_editor ();
1298 /* reverse this here so that we have the correct logic to finalize
1302 if (Config->get_edit_mode() == Lock) {
1303 _x_constrained = !_x_constrained;
1306 assert (!_views.empty ());
1308 /* We might have hidden region views so that they weren't visible during the drag
1309 (when they have been reparented). Now everything can be shown again, as region
1310 views are back in their track parent groups.
1312 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1313 i->view->get_canvas_group()->show ();
1316 bool const changed_position = (_last_frame_position != _primary->region()->position());
1317 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1318 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1338 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1342 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1344 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1349 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1350 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1351 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1352 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1354 rtav->set_height (original->current_height());
1358 ChanCount one_midi_port (DataType::MIDI, 1);
1359 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1360 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1361 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1363 rtav->set_height (original->current_height());
1368 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1374 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1376 RegionSelection new_views;
1377 PlaylistSet modified_playlists;
1378 RouteTimeAxisView* new_time_axis_view = 0;
1381 /* all changes were made during motion event handlers */
1383 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1387 _editor->commit_reversible_command ();
1391 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1392 PlaylistMapping playlist_mapping;
1394 /* insert the regions into their new playlists */
1395 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1397 RouteTimeAxisView* dest_rtv = 0;
1399 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1405 if (changed_position && !_x_constrained) {
1406 where = i->view->region()->position() - drag_delta;
1408 where = i->view->region()->position();
1411 if (i->time_axis_view < 0 || i->time_axis_view >= _time_axis_views.size()) {
1412 /* dragged to drop zone */
1414 PlaylistMapping::iterator pm;
1416 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1417 /* first region from this original playlist: create a new track */
1418 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1419 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1420 dest_rtv = new_time_axis_view;
1422 /* we already created a new track for regions from this playlist, use it */
1423 dest_rtv = pm->second;
1426 /* destination time axis view is the one we dragged to */
1427 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1430 if (dest_rtv != 0) {
1431 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1432 if (new_view != 0) {
1433 new_views.push_back (new_view);
1437 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1438 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1441 list<DraggingView>::const_iterator next = i;
1447 /* If we've created new regions either by copying or moving
1448 to a new track, we want to replace the old selection with the new ones
1451 if (new_views.size() > 0) {
1452 _editor->selection->set (new_views);
1455 /* write commands for the accumulated diffs for all our modified playlists */
1456 add_stateful_diff_commands_for_playlists (modified_playlists);
1458 _editor->commit_reversible_command ();
1462 RegionMoveDrag::finished_no_copy (
1463 bool const changed_position,
1464 bool const changed_tracks,
1465 framecnt_t const drag_delta
1468 RegionSelection new_views;
1469 PlaylistSet modified_playlists;
1470 PlaylistSet frozen_playlists;
1471 set<RouteTimeAxisView*> views_to_update;
1472 RouteTimeAxisView* new_time_axis_view = 0;
1475 /* all changes were made during motion event handlers */
1476 _editor->commit_reversible_command ();
1480 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1481 PlaylistMapping playlist_mapping;
1483 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1485 RegionView* rv = i->view;
1486 RouteTimeAxisView* dest_rtv = 0;
1488 if (rv->region()->locked() || rv->region()->video_locked()) {
1493 if (i->time_axis_view < 0 || i->time_axis_view >= _time_axis_views.size()) {
1494 /* dragged to drop zone */
1496 PlaylistMapping::iterator pm;
1498 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1499 /* first region from this original playlist: create a new track */
1500 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1501 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1502 dest_rtv = new_time_axis_view;
1504 /* we already created a new track for regions from this playlist, use it */
1505 dest_rtv = pm->second;
1509 /* destination time axis view is the one we dragged to */
1510 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1515 double const dest_layer = i->layer;
1517 views_to_update.insert (dest_rtv);
1521 if (changed_position && !_x_constrained) {
1522 where = rv->region()->position() - drag_delta;
1524 where = rv->region()->position();
1527 if (changed_tracks) {
1529 /* insert into new playlist */
1531 RegionView* new_view = insert_region_into_playlist (
1532 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1535 if (new_view == 0) {
1540 new_views.push_back (new_view);
1542 /* remove from old playlist */
1544 /* the region that used to be in the old playlist is not
1545 moved to the new one - we use a copy of it. as a result,
1546 any existing editor for the region should no longer be
1549 rv->hide_region_editor();
1552 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1556 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1558 /* this movement may result in a crossfade being modified, or a layering change,
1559 so we need to get undo data from the playlist as well as the region.
1562 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1564 playlist->clear_changes ();
1567 rv->region()->clear_changes ();
1570 motion on the same track. plonk the previously reparented region
1571 back to its original canvas group (its streamview).
1572 No need to do anything for copies as they are fake regions which will be deleted.
1575 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1576 rv->get_canvas_group()->set_y_position (i->initial_y);
1579 /* just change the model */
1580 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1581 playlist->set_layer (rv->region(), dest_layer);
1584 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1586 r = frozen_playlists.insert (playlist);
1589 playlist->freeze ();
1592 rv->region()->set_position (where);
1594 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1597 if (changed_tracks) {
1599 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1600 was selected in all of them, then removing it from a playlist will have removed all
1601 trace of it from _views (i.e. there were N regions selected, we removed 1,
1602 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1603 corresponding regionview, and _views is now empty).
1605 This could have invalidated any and all iterators into _views.
1607 The heuristic we use here is: if the region selection is empty, break out of the loop
1608 here. if the region selection is not empty, then restart the loop because we know that
1609 we must have removed at least the region(view) we've just been working on as well as any
1610 that we processed on previous iterations.
1612 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1613 we can just iterate.
1617 if (_views.empty()) {
1628 /* If we've created new regions either by copying or moving
1629 to a new track, we want to replace the old selection with the new ones
1632 if (new_views.size() > 0) {
1633 _editor->selection->set (new_views);
1636 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1640 /* write commands for the accumulated diffs for all our modified playlists */
1641 add_stateful_diff_commands_for_playlists (modified_playlists);
1643 _editor->commit_reversible_command ();
1645 /* We have futzed with the layering of canvas items on our streamviews.
1646 If any region changed layer, this will have resulted in the stream
1647 views being asked to set up their region views, and all will be well.
1648 If not, we might now have badly-ordered region views. Ask the StreamViews
1649 involved to sort themselves out, just in case.
1652 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1653 (*i)->view()->playlist_layered ((*i)->track ());
1657 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1658 * @param region Region to remove.
1659 * @param playlist playlist To remove from.
1660 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1661 * that clear_changes () is only called once per playlist.
1664 RegionMoveDrag::remove_region_from_playlist (
1665 boost::shared_ptr<Region> region,
1666 boost::shared_ptr<Playlist> playlist,
1667 PlaylistSet& modified_playlists
1670 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1673 playlist->clear_changes ();
1676 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1680 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1681 * clearing the playlist's diff history first if necessary.
1682 * @param region Region to insert.
1683 * @param dest_rtv Destination RouteTimeAxisView.
1684 * @param dest_layer Destination layer.
1685 * @param where Destination position.
1686 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1687 * that clear_changes () is only called once per playlist.
1688 * @return New RegionView, or 0 if no insert was performed.
1691 RegionMoveDrag::insert_region_into_playlist (
1692 boost::shared_ptr<Region> region,
1693 RouteTimeAxisView* dest_rtv,
1696 PlaylistSet& modified_playlists
1699 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1700 if (!dest_playlist) {
1704 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1705 _new_region_view = 0;
1706 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1708 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1709 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1711 dest_playlist->clear_changes ();
1714 dest_playlist->add_region (region, where);
1716 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1717 dest_playlist->set_layer (region, dest_layer);
1722 assert (_new_region_view);
1724 return _new_region_view;
1728 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1730 _new_region_view = rv;
1734 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1736 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1737 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1739 _editor->session()->add_command (c);
1748 RegionMoveDrag::aborted (bool movement_occurred)
1752 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1753 list<DraggingView>::const_iterator next = i;
1762 RegionMotionDrag::aborted (movement_occurred);
1767 RegionMotionDrag::aborted (bool)
1769 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1771 StreamView* sview = (*i)->view();
1774 if (sview->layer_display() == Expanded) {
1775 sview->set_layer_display (Stacked);
1780 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1781 RegionView* rv = i->view;
1782 TimeAxisView* tv = &(rv->get_time_axis_view ());
1783 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1785 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1786 rv->get_canvas_group()->set_y_position (0);
1788 rv->move (-_total_x_delta, 0);
1789 rv->set_height (rtv->view()->child_height ());
1793 /** @param b true to brush, otherwise false.
1794 * @param c true to make copies of the regions being moved, otherwise false.
1796 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1797 : RegionMotionDrag (e, i, p, v, b)
1800 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1803 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1804 if (rtv && rtv->is_track()) {
1805 speed = rtv->track()->speed ();
1808 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1812 RegionMoveDrag::setup_pointer_frame_offset ()
1814 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1817 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1818 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1820 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1822 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1823 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1825 _primary = v->view()->create_region_view (r, false, false);
1827 _primary->get_canvas_group()->show ();
1828 _primary->set_position (pos, 0);
1829 _views.push_back (DraggingView (_primary, this, v));
1831 _last_frame_position = pos;
1833 _item = _primary->get_canvas_group ();
1837 RegionInsertDrag::finished (GdkEvent *, bool)
1839 int pos = _views.front().time_axis_view;
1840 assert(pos >= 0 && pos < _time_axis_views.size());
1842 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1844 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1845 _primary->get_canvas_group()->set_y_position (0);
1847 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1849 _editor->begin_reversible_command (Operations::insert_region);
1850 playlist->clear_changes ();
1851 playlist->add_region (_primary->region (), _last_frame_position);
1853 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1854 if (Config->get_edit_mode() == Ripple) {
1855 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1858 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1859 _editor->commit_reversible_command ();
1867 RegionInsertDrag::aborted (bool)
1874 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1875 : RegionMoveDrag (e, i, p, v, false, false)
1877 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1880 struct RegionSelectionByPosition {
1881 bool operator() (RegionView*a, RegionView* b) {
1882 return a->region()->position () < b->region()->position();
1887 RegionSpliceDrag::motion (GdkEvent* event, bool)
1889 /* Which trackview is this ? */
1891 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1892 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1894 /* The region motion is only processed if the pointer is over
1898 if (!tv || !tv->is_track()) {
1899 /* To make sure we hide the verbose canvas cursor when the mouse is
1900 not held over an audio track.
1902 _editor->verbose_cursor()->hide ();
1905 _editor->verbose_cursor()->show ();
1910 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1916 RegionSelection copy;
1917 _editor->selection->regions.by_position(copy);
1919 framepos_t const pf = adjusted_current_frame (event);
1921 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1923 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1929 boost::shared_ptr<Playlist> playlist;
1931 if ((playlist = atv->playlist()) == 0) {
1935 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1940 if (pf < (*i)->region()->last_frame() + 1) {
1944 if (pf > (*i)->region()->first_frame()) {
1950 playlist->shuffle ((*i)->region(), dir);
1955 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1957 RegionMoveDrag::finished (event, movement_occurred);
1961 RegionSpliceDrag::aborted (bool)
1971 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1974 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1976 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1977 RegionSelection to_ripple;
1978 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1979 if ((*i)->position() >= where) {
1980 to_ripple.push_back (rtv->view()->find_view(*i));
1984 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1985 if (!exclude.contains (*i)) {
1986 // the selection has already been added to _views
1988 if (drag_in_progress) {
1989 // do the same things that RegionMotionDrag::motion does when
1990 // first_move is true, for the region views that we're adding
1991 // to _views this time
1994 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1995 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1996 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1997 rvg->reparent (_editor->_drag_motion_group);
1999 // we only need to move in the y direction
2000 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2005 _views.push_back (DraggingView (*i, this, tav));
2011 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2014 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2015 // we added all the regions after the selection
2017 std::list<DraggingView>::iterator to_erase = i++;
2018 if (!_editor->selection->regions.contains (to_erase->view)) {
2019 // restore the non-selected regions to their original playlist & positions,
2020 // and then ripple them back by the length of the regions that were dragged away
2021 // do the same things as RegionMotionDrag::aborted
2023 RegionView *rv = to_erase->view;
2024 TimeAxisView* tv = &(rv->get_time_axis_view ());
2025 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2028 // plonk them back onto their own track
2029 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2030 rv->get_canvas_group()->set_y_position (0);
2034 // move the underlying region to match the view
2035 rv->region()->set_position (rv->region()->position() + amount);
2037 // restore the view to match the underlying region's original position
2038 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2041 rv->set_height (rtv->view()->child_height ());
2042 _views.erase (to_erase);
2048 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2050 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2052 return allow_moves_across_tracks;
2060 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2061 : RegionMoveDrag (e, i, p, v, false, false)
2063 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2064 // compute length of selection
2065 RegionSelection selected_regions = _editor->selection->regions;
2066 selection_length = selected_regions.end_frame() - selected_regions.start();
2068 // we'll only allow dragging to another track in ripple mode if all the regions
2069 // being dragged start off on the same track
2070 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2073 exclude = new RegionList;
2074 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2075 exclude->push_back((*i)->region());
2078 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2079 RegionSelection copy;
2080 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2082 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2083 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2085 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2086 // find ripple start point on each applicable playlist
2087 RegionView *first_selected_on_this_track = NULL;
2088 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2089 if ((*i)->region()->playlist() == (*pi)) {
2090 // region is on this playlist - it's the first, because they're sorted
2091 first_selected_on_this_track = *i;
2095 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2096 add_all_after_to_views (
2097 &first_selected_on_this_track->get_time_axis_view(),
2098 first_selected_on_this_track->region()->position(),
2099 selected_regions, false);
2102 if (allow_moves_across_tracks) {
2103 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2111 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2113 /* Which trackview is this ? */
2115 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2116 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2118 /* The region motion is only processed if the pointer is over
2122 if (!tv || !tv->is_track()) {
2123 /* To make sure we hide the verbose canvas cursor when the mouse is
2124 not held over an audiotrack.
2126 _editor->verbose_cursor()->hide ();
2130 framepos_t where = adjusted_current_frame (event);
2131 assert (where >= 0);
2133 double delta = compute_x_delta (event, &after);
2135 framecnt_t amount = _editor->pixel_to_sample (delta);
2137 if (allow_moves_across_tracks) {
2138 // all the originally selected regions were on the same track
2140 framecnt_t adjust = 0;
2141 if (prev_tav && tv != prev_tav) {
2142 // dragged onto a different track
2143 // remove the unselected regions from _views, restore them to their original positions
2144 // and add the regions after the drop point on the new playlist to _views instead.
2145 // undo the effect of rippling the previous playlist, and include the effect of removing
2146 // the dragged region(s) from this track
2148 remove_unselected_from_views (prev_amount, false);
2149 // ripple previous playlist according to the regions that have been removed onto the new playlist
2150 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2153 // move just the selected regions
2154 RegionMoveDrag::motion(event, first_move);
2156 // ensure that the ripple operation on the new playlist inserts selection_length time
2157 adjust = selection_length;
2158 // ripple the new current playlist
2159 tv->playlist()->ripple (where, amount+adjust, exclude);
2161 // add regions after point where drag entered this track to subsequent ripples
2162 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2165 // motion on same track
2166 RegionMoveDrag::motion(event, first_move);
2170 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2171 prev_position = where;
2173 // selection encompasses multiple tracks - just drag
2174 // cross-track drags are forbidden
2175 RegionMoveDrag::motion(event, first_move);
2178 if (!_x_constrained) {
2179 prev_amount += amount;
2182 _last_frame_position = after;
2186 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2188 if (!movement_occurred) {
2192 if (was_double_click() && !_views.empty()) {
2193 DraggingView dv = _views.front();
2194 dv.view->show_region_editor ();
2201 _editor->begin_reversible_command(_("Ripple drag"));
2203 // remove the regions being rippled from the dragging view, updating them to
2204 // their new positions
2205 remove_unselected_from_views (prev_amount, true);
2207 if (allow_moves_across_tracks) {
2209 // if regions were dragged across tracks, we've rippled any later
2210 // regions on the track the regions were dragged off, so we need
2211 // to add the original track to the undo record
2212 orig_tav->playlist()->clear_changes();
2213 vector<Command*> cmds;
2214 orig_tav->playlist()->rdiff (cmds);
2215 _editor->session()->add_commands (cmds);
2217 if (prev_tav && prev_tav != orig_tav) {
2218 prev_tav->playlist()->clear_changes();
2219 vector<Command*> cmds;
2220 prev_tav->playlist()->rdiff (cmds);
2221 _editor->session()->add_commands (cmds);
2224 // selection spanned multiple tracks - all will need adding to undo record
2226 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2227 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2229 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2230 (*pi)->clear_changes();
2231 vector<Command*> cmds;
2232 (*pi)->rdiff (cmds);
2233 _editor->session()->add_commands (cmds);
2237 // other modified playlists are added to undo by RegionMoveDrag::finished()
2238 RegionMoveDrag::finished (event, movement_occurred);
2239 _editor->commit_reversible_command();
2243 RegionRippleDrag::aborted (bool movement_occurred)
2245 RegionMoveDrag::aborted (movement_occurred);
2250 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2252 _view (dynamic_cast<MidiTimeAxisView*> (v))
2254 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2260 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2263 _region = add_midi_region (_view);
2264 _view->playlist()->freeze ();
2267 framepos_t const f = adjusted_current_frame (event);
2268 if (f < grab_frame()) {
2269 _region->set_position (f);
2272 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2273 so that if this region is duplicated, its duplicate starts on
2274 a snap point rather than 1 frame after a snap point. Otherwise things get
2275 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2276 place snapped notes at the start of the region.
2279 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2280 _region->set_length (len < 1 ? 1 : len);
2286 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2288 if (!movement_occurred) {
2289 add_midi_region (_view);
2291 _view->playlist()->thaw ();
2296 RegionCreateDrag::aborted (bool)
2299 _view->playlist()->thaw ();
2305 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2309 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2313 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2315 Gdk::Cursor* cursor;
2316 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2318 float x_fraction = cnote->mouse_x_fraction ();
2320 if (x_fraction > 0.0 && x_fraction < 0.25) {
2321 cursor = _editor->cursors()->left_side_trim;
2324 cursor = _editor->cursors()->right_side_trim;
2328 Drag::start_grab (event, cursor);
2330 region = &cnote->region_view();
2334 if (event->motion.state & Keyboard::PrimaryModifier) {
2340 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2342 if (ms.size() > 1) {
2343 /* has to be relative, may make no sense otherwise */
2347 /* select this note; if it is already selected, preserve the existing selection,
2348 otherwise make this note the only one selected.
2350 region->note_selected (cnote, cnote->selected ());
2352 _editor->begin_reversible_command (_("resize notes"));
2354 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2355 MidiRegionSelection::iterator next;
2358 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2360 mrv->begin_resizing (at_front);
2367 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
2369 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2370 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2371 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2373 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2375 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2381 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
2383 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2384 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2385 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2387 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2389 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2393 _editor->commit_reversible_command ();
2397 NoteResizeDrag::aborted (bool)
2399 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2400 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2401 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2403 mrv->abort_resizing ();
2408 AVDraggingView::AVDraggingView (RegionView* v)
2411 initial_position = v->region()->position ();
2414 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2417 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2420 TrackViewList empty;
2422 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2423 std::list<RegionView*> views = rs.by_layer();
2425 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2426 RegionView* rv = (*i);
2427 if (!rv->region()->video_locked()) {
2430 _views.push_back (AVDraggingView (rv));
2435 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2437 Drag::start_grab (event);
2438 if (_editor->session() == 0) {
2442 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2443 _max_backwards_drag = (
2444 ARDOUR_UI::instance()->video_timeline->get_duration()
2445 + ARDOUR_UI::instance()->video_timeline->get_offset()
2446 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2449 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2450 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2451 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2454 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2457 Timecode::Time timecode;
2458 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2459 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);
2460 show_verbose_cursor_text (buf);
2464 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2466 if (_editor->session() == 0) {
2469 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2473 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2474 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2476 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2477 dt = - _max_backwards_drag;
2480 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2481 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2483 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2484 RegionView* rv = i->view;
2485 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2488 rv->region()->clear_changes ();
2489 rv->region()->suspend_property_changes();
2491 rv->region()->set_position(i->initial_position + dt);
2492 rv->region_changed(ARDOUR::Properties::position);
2495 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2496 Timecode::Time timecode;
2497 Timecode::Time timediff;
2499 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2500 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2501 snprintf (buf, sizeof (buf),
2502 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2503 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2504 , _("Video Start:"),
2505 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2507 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2509 show_verbose_cursor_text (buf);
2513 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2515 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2519 if (!movement_occurred || ! _editor->session()) {
2523 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2525 _editor->begin_reversible_command (_("Move Video"));
2527 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2528 ARDOUR_UI::instance()->video_timeline->save_undo();
2529 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2530 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2532 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2533 i->view->drag_end();
2534 i->view->region()->resume_property_changes ();
2536 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2539 _editor->session()->maybe_update_session_range(
2540 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2541 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2545 _editor->commit_reversible_command ();
2549 VideoTimeLineDrag::aborted (bool)
2551 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2554 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2555 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2557 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2558 i->view->region()->resume_property_changes ();
2559 i->view->region()->set_position(i->initial_position);
2563 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2564 : RegionDrag (e, i, p, v)
2565 , _preserve_fade_anchor (preserve_fade_anchor)
2566 , _jump_position_when_done (false)
2568 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2572 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2575 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2576 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2578 if (tv && tv->is_track()) {
2579 speed = tv->track()->speed();
2582 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2583 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2584 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2586 framepos_t const pf = adjusted_current_frame (event);
2588 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2589 /* Move the contents of the region around without changing the region bounds */
2590 _operation = ContentsTrim;
2591 Drag::start_grab (event, _editor->cursors()->trimmer);
2593 /* These will get overridden for a point trim.*/
2594 if (pf < (region_start + region_length/2)) {
2595 /* closer to front */
2596 _operation = StartTrim;
2598 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2599 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2601 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2605 _operation = EndTrim;
2606 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2607 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2609 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2614 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2615 _jump_position_when_done = true;
2618 switch (_operation) {
2620 show_verbose_cursor_time (region_start);
2621 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2622 i->view->trim_front_starting ();
2626 show_verbose_cursor_time (region_end);
2629 show_verbose_cursor_time (pf);
2633 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2634 i->view->region()->suspend_property_changes ();
2639 TrimDrag::motion (GdkEvent* event, bool first_move)
2641 RegionView* rv = _primary;
2644 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2645 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2646 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2647 frameoffset_t frame_delta = 0;
2649 if (tv && tv->is_track()) {
2650 speed = tv->track()->speed();
2653 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2659 switch (_operation) {
2661 trim_type = "Region start trim";
2664 trim_type = "Region end trim";
2667 trim_type = "Region content trim";
2674 _editor->begin_reversible_command (trim_type);
2676 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2677 RegionView* rv = i->view;
2678 rv->enable_display (false);
2679 rv->region()->playlist()->clear_owned_changes ();
2681 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2684 arv->temporarily_hide_envelope ();
2688 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2689 insert_result = _editor->motion_frozen_playlists.insert (pl);
2691 if (insert_result.second) {
2697 bool non_overlap_trim = false;
2699 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2700 non_overlap_trim = true;
2703 /* contstrain trim to fade length */
2704 if (_preserve_fade_anchor) {
2705 switch (_operation) {
2707 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2708 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2710 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2711 if (ar->locked()) continue;
2712 framecnt_t len = ar->fade_in()->back()->when;
2713 if (len < dt) dt = min(dt, len);
2717 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2718 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2720 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2721 if (ar->locked()) continue;
2722 framecnt_t len = ar->fade_out()->back()->when;
2723 if (len < -dt) dt = max(dt, -len);
2732 switch (_operation) {
2734 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2735 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2736 if (changed && _preserve_fade_anchor) {
2737 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2739 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2740 framecnt_t len = ar->fade_in()->back()->when;
2741 framecnt_t diff = ar->first_frame() - i->initial_position;
2742 framepos_t new_length = len - diff;
2743 i->anchored_fade_length = min (ar->length(), new_length);
2744 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2745 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2752 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2753 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2754 if (changed && _preserve_fade_anchor) {
2755 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2757 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2758 framecnt_t len = ar->fade_out()->back()->when;
2759 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2760 framepos_t new_length = len + diff;
2761 i->anchored_fade_length = min (ar->length(), new_length);
2762 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2763 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2771 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2773 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2774 i->view->move_contents (frame_delta);
2780 switch (_operation) {
2782 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2785 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2788 // show_verbose_cursor_time (frame_delta);
2795 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2797 if (movement_occurred) {
2798 motion (event, false);
2800 if (_operation == StartTrim) {
2801 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2803 /* This must happen before the region's StatefulDiffCommand is created, as it may
2804 `correct' (ahem) the region's _start from being negative to being zero. It
2805 needs to be zero in the undo record.
2807 i->view->trim_front_ending ();
2809 if (_preserve_fade_anchor) {
2810 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2812 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2813 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2814 ar->set_fade_in_length(i->anchored_fade_length);
2815 ar->set_fade_in_active(true);
2818 if (_jump_position_when_done) {
2819 i->view->region()->set_position (i->initial_position);
2822 } else if (_operation == EndTrim) {
2823 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2824 if (_preserve_fade_anchor) {
2825 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2827 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2828 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2829 ar->set_fade_out_length(i->anchored_fade_length);
2830 ar->set_fade_out_active(true);
2833 if (_jump_position_when_done) {
2834 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2839 if (!_views.empty()) {
2840 if (_operation == StartTrim) {
2841 _editor->maybe_locate_with_edit_preroll(
2842 _views.begin()->view->region()->position());
2844 if (_operation == EndTrim) {
2845 _editor->maybe_locate_with_edit_preroll(
2846 _views.begin()->view->region()->position() +
2847 _views.begin()->view->region()->length());
2851 if (!_editor->selection->selected (_primary)) {
2852 _primary->thaw_after_trim ();
2855 set<boost::shared_ptr<Playlist> > diffed_playlists;
2857 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2858 i->view->thaw_after_trim ();
2859 i->view->enable_display (true);
2861 /* Trimming one region may affect others on the playlist, so we need
2862 to get undo Commands from the whole playlist rather than just the
2863 region. Use diffed_playlists to make sure we don't diff a given
2864 playlist more than once.
2866 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2867 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2868 vector<Command*> cmds;
2870 _editor->session()->add_commands (cmds);
2871 diffed_playlists.insert (p);
2876 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2880 _editor->motion_frozen_playlists.clear ();
2881 _editor->commit_reversible_command();
2884 /* no mouse movement */
2885 _editor->point_trim (event, adjusted_current_frame (event));
2888 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2889 if (_operation == StartTrim) {
2890 i->view->trim_front_ending ();
2893 i->view->region()->resume_property_changes ();
2898 TrimDrag::aborted (bool movement_occurred)
2900 /* Our motion method is changing model state, so use the Undo system
2901 to cancel. Perhaps not ideal, as this will leave an Undo point
2902 behind which may be slightly odd from the user's point of view.
2907 if (movement_occurred) {
2911 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2912 i->view->region()->resume_property_changes ();
2917 TrimDrag::setup_pointer_frame_offset ()
2919 list<DraggingView>::iterator i = _views.begin ();
2920 while (i != _views.end() && i->view != _primary) {
2924 if (i == _views.end()) {
2928 switch (_operation) {
2930 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2933 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2940 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2944 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2945 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2950 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2952 Drag::start_grab (event, cursor);
2953 show_verbose_cursor_time (adjusted_current_frame(event));
2957 MeterMarkerDrag::setup_pointer_frame_offset ()
2959 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2963 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2965 if (!_marker->meter().movable()) {
2971 // create a dummy marker for visual representation of moving the
2972 // section, because whether its a copy or not, we're going to
2973 // leave or lose the original marker (leave if its a copy; lose if its
2974 // not, because we'll remove it from the map).
2976 MeterSection section (_marker->meter());
2978 if (!section.movable()) {
2983 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2985 _marker = new MeterMarker (
2987 *_editor->meter_group,
2988 ARDOUR_UI::config()->color ("meter marker"),
2990 *new MeterSection (_marker->meter())
2993 /* use the new marker for the grab */
2994 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2997 TempoMap& map (_editor->session()->tempo_map());
2998 /* get current state */
2999 before_state = &map.get_state();
3000 /* remove the section while we drag it */
3001 map.remove_meter (section, true);
3005 framepos_t const pf = adjusted_current_frame (event);
3007 _marker->set_position (pf);
3008 show_verbose_cursor_time (pf);
3012 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3014 if (!movement_occurred) {
3015 if (was_double_click()) {
3016 _editor->edit_meter_marker (*_marker);
3021 if (!_marker->meter().movable()) {
3025 motion (event, false);
3027 Timecode::BBT_Time when;
3029 TempoMap& map (_editor->session()->tempo_map());
3030 map.bbt_time (last_pointer_frame(), when);
3032 if (_copy == true) {
3033 _editor->begin_reversible_command (_("copy meter mark"));
3034 XMLNode &before = map.get_state();
3035 map.add_meter (_marker->meter(), when);
3036 XMLNode &after = map.get_state();
3037 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3038 _editor->commit_reversible_command ();
3041 _editor->begin_reversible_command (_("move meter mark"));
3043 /* we removed it before, so add it back now */
3045 map.add_meter (_marker->meter(), when);
3046 XMLNode &after = map.get_state();
3047 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3048 _editor->commit_reversible_command ();
3051 // delete the dummy marker we used for visual representation while moving.
3052 // a new visual marker will show up automatically.
3057 MeterMarkerDrag::aborted (bool moved)
3059 _marker->set_position (_marker->meter().frame ());
3062 TempoMap& map (_editor->session()->tempo_map());
3063 /* we removed it before, so add it back now */
3064 map.add_meter (_marker->meter(), _marker->meter().frame());
3065 // delete the dummy marker we used for visual representation while moving.
3066 // a new visual marker will show up automatically.
3071 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3075 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3077 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3082 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3084 Drag::start_grab (event, cursor);
3085 show_verbose_cursor_time (adjusted_current_frame (event));
3089 TempoMarkerDrag::setup_pointer_frame_offset ()
3091 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3095 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3097 if (!_marker->tempo().movable()) {
3103 // create a dummy marker for visual representation of moving the
3104 // section, because whether its a copy or not, we're going to
3105 // leave or lose the original marker (leave if its a copy; lose if its
3106 // not, because we'll remove it from the map).
3108 // create a dummy marker for visual representation of moving the copy.
3109 // The actual copying is not done before we reach the finish callback.
3112 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3114 TempoSection section (_marker->tempo());
3116 _marker = new TempoMarker (
3118 *_editor->tempo_group,
3119 ARDOUR_UI::config()->color ("tempo marker"),
3121 *new TempoSection (_marker->tempo())
3124 /* use the new marker for the grab */
3125 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3128 TempoMap& map (_editor->session()->tempo_map());
3129 /* get current state */
3130 before_state = &map.get_state();
3131 /* remove the section while we drag it */
3132 map.remove_tempo (section, true);
3136 framepos_t const pf = adjusted_current_frame (event);
3137 _marker->set_position (pf);
3138 show_verbose_cursor_time (pf);
3142 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3144 if (!movement_occurred) {
3145 if (was_double_click()) {
3146 _editor->edit_tempo_marker (*_marker);
3151 if (!_marker->tempo().movable()) {
3155 motion (event, false);
3157 TempoMap& map (_editor->session()->tempo_map());
3158 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3159 Timecode::BBT_Time when;
3161 map.bbt_time (beat_time, when);
3163 if (_copy == true) {
3164 _editor->begin_reversible_command (_("copy tempo mark"));
3165 XMLNode &before = map.get_state();
3166 map.add_tempo (_marker->tempo(), when);
3167 XMLNode &after = map.get_state();
3168 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3169 _editor->commit_reversible_command ();
3172 _editor->begin_reversible_command (_("move tempo mark"));
3173 /* we removed it before, so add it back now */
3174 map.add_tempo (_marker->tempo(), when);
3175 XMLNode &after = map.get_state();
3176 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3177 _editor->commit_reversible_command ();
3180 // delete the dummy marker we used for visual representation while moving.
3181 // a new visual marker will show up automatically.
3186 TempoMarkerDrag::aborted (bool moved)
3188 _marker->set_position (_marker->tempo().frame());
3190 TempoMap& map (_editor->session()->tempo_map());
3191 /* we removed it before, so add it back now */
3192 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3193 // delete the dummy marker we used for visual representation while moving.
3194 // a new visual marker will show up automatically.
3199 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3200 : Drag (e, &c.track_canvas_item(), false)
3204 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3207 /** Do all the things we do when dragging the playhead to make it look as though
3208 * we have located, without actually doing the locate (because that would cause
3209 * the diskstream buffers to be refilled, which is too slow).
3212 CursorDrag::fake_locate (framepos_t t)
3214 _editor->playhead_cursor->set_position (t);
3216 Session* s = _editor->session ();
3217 if (s->timecode_transmission_suspended ()) {
3218 framepos_t const f = _editor->playhead_cursor->current_frame ();
3219 /* This is asynchronous so it will be sent "now"
3221 s->send_mmc_locate (f);
3222 /* These are synchronous and will be sent during the next
3225 s->queue_full_time_code ();
3226 s->queue_song_position_pointer ();
3229 show_verbose_cursor_time (t);
3230 _editor->UpdateAllTransportClocks (t);
3234 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3236 Drag::start_grab (event, c);
3238 _grab_zoom = _editor->samples_per_pixel;
3240 framepos_t where = _editor->canvas_event_sample (event);
3242 _editor->snap_to_with_modifier (where, event);
3244 _editor->_dragging_playhead = true;
3246 Session* s = _editor->session ();
3248 /* grab the track canvas item as well */
3250 _cursor.track_canvas_item().grab();
3253 if (_was_rolling && _stop) {
3257 if (s->is_auditioning()) {
3258 s->cancel_audition ();
3262 if (AudioEngine::instance()->connected()) {
3264 /* do this only if we're the engine is connected
3265 * because otherwise this request will never be
3266 * serviced and we'll busy wait forever. likewise,
3267 * notice if we are disconnected while waiting for the
3268 * request to be serviced.
3271 s->request_suspend_timecode_transmission ();
3272 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3273 /* twiddle our thumbs */
3278 fake_locate (where);
3282 CursorDrag::motion (GdkEvent* event, bool)
3284 framepos_t const adjusted_frame = adjusted_current_frame (event);
3285 if (adjusted_frame != last_pointer_frame()) {
3286 fake_locate (adjusted_frame);
3291 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3293 _editor->_dragging_playhead = false;
3295 _cursor.track_canvas_item().ungrab();
3297 if (!movement_occurred && _stop) {
3301 motion (event, false);
3303 Session* s = _editor->session ();
3305 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3306 _editor->_pending_locate_request = true;
3307 s->request_resume_timecode_transmission ();
3312 CursorDrag::aborted (bool)
3314 _cursor.track_canvas_item().ungrab();
3316 if (_editor->_dragging_playhead) {
3317 _editor->session()->request_resume_timecode_transmission ();
3318 _editor->_dragging_playhead = false;
3321 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3324 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3325 : RegionDrag (e, i, p, v)
3327 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3331 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3333 Drag::start_grab (event, cursor);
3335 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3336 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3338 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3342 FadeInDrag::setup_pointer_frame_offset ()
3344 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3345 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3346 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3350 FadeInDrag::motion (GdkEvent* event, bool)
3352 framecnt_t fade_length;
3353 framepos_t const pos = adjusted_current_frame (event);
3354 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3356 if (pos < (region->position() + 64)) {
3357 fade_length = 64; // this should be a minimum defined somewhere
3358 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3359 fade_length = region->length() - region->fade_out()->back()->when - 1;
3361 fade_length = pos - region->position();
3364 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3366 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3372 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3375 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3379 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3381 if (!movement_occurred) {
3385 framecnt_t fade_length;
3387 framepos_t const pos = adjusted_current_frame (event);
3389 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3391 if (pos < (region->position() + 64)) {
3392 fade_length = 64; // this should be a minimum defined somewhere
3393 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3394 fade_length = region->length() - region->fade_out()->back()->when - 1;
3396 fade_length = pos - region->position();
3399 _editor->begin_reversible_command (_("change fade in length"));
3401 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3403 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3409 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3410 XMLNode &before = alist->get_state();
3412 tmp->audio_region()->set_fade_in_length (fade_length);
3413 tmp->audio_region()->set_fade_in_active (true);
3415 XMLNode &after = alist->get_state();
3416 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3419 _editor->commit_reversible_command ();
3423 FadeInDrag::aborted (bool)
3425 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3426 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3432 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3436 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3437 : RegionDrag (e, i, p, v)
3439 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3443 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3445 Drag::start_grab (event, cursor);
3447 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3448 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3450 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3454 FadeOutDrag::setup_pointer_frame_offset ()
3456 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3457 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3458 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3462 FadeOutDrag::motion (GdkEvent* event, bool)
3464 framecnt_t fade_length;
3466 framepos_t const pos = adjusted_current_frame (event);
3468 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3470 if (pos > (region->last_frame() - 64)) {
3471 fade_length = 64; // this should really be a minimum fade defined somewhere
3472 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3473 fade_length = region->length() - region->fade_in()->back()->when - 1;
3475 fade_length = region->last_frame() - pos;
3478 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3480 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3486 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3489 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3493 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3495 if (!movement_occurred) {
3499 framecnt_t fade_length;
3501 framepos_t const pos = adjusted_current_frame (event);
3503 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3505 if (pos > (region->last_frame() - 64)) {
3506 fade_length = 64; // this should really be a minimum fade defined somewhere
3507 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3508 fade_length = region->length() - region->fade_in()->back()->when - 1;
3510 fade_length = region->last_frame() - pos;
3513 _editor->begin_reversible_command (_("change fade out length"));
3515 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3517 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3523 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3524 XMLNode &before = alist->get_state();
3526 tmp->audio_region()->set_fade_out_length (fade_length);
3527 tmp->audio_region()->set_fade_out_active (true);
3529 XMLNode &after = alist->get_state();
3530 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3533 _editor->commit_reversible_command ();
3537 FadeOutDrag::aborted (bool)
3539 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3540 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3546 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3550 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3553 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3555 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3558 _points.push_back (ArdourCanvas::Duple (0, 0));
3559 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3562 MarkerDrag::~MarkerDrag ()
3564 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3569 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3571 location = new Location (*l);
3572 markers.push_back (m);
3577 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3579 Drag::start_grab (event, cursor);
3583 Location *location = _editor->find_location_from_marker (_marker, is_start);
3584 _editor->_dragging_edit_point = true;
3586 update_item (location);
3588 // _drag_line->show();
3589 // _line->raise_to_top();
3592 show_verbose_cursor_time (location->start());
3594 show_verbose_cursor_time (location->end());
3597 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3600 case Selection::Toggle:
3601 /* we toggle on the button release */
3603 case Selection::Set:
3604 if (!_editor->selection->selected (_marker)) {
3605 _editor->selection->set (_marker);
3608 case Selection::Extend:
3610 Locations::LocationList ll;
3611 list<Marker*> to_add;
3613 _editor->selection->markers.range (s, e);
3614 s = min (_marker->position(), s);
3615 e = max (_marker->position(), e);
3618 if (e < max_framepos) {
3621 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3622 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3623 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3626 to_add.push_back (lm->start);
3629 to_add.push_back (lm->end);
3633 if (!to_add.empty()) {
3634 _editor->selection->add (to_add);
3638 case Selection::Add:
3639 _editor->selection->add (_marker);
3643 /* Set up copies for us to manipulate during the drag
3646 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3648 Location* l = _editor->find_location_from_marker (*i, is_start);
3655 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3657 /* range: check that the other end of the range isn't
3660 CopiedLocationInfo::iterator x;
3661 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3662 if (*(*x).location == *l) {
3666 if (x == _copied_locations.end()) {
3667 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3669 (*x).markers.push_back (*i);
3670 (*x).move_both = true;
3678 MarkerDrag::setup_pointer_frame_offset ()
3681 Location *location = _editor->find_location_from_marker (_marker, is_start);
3682 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3686 MarkerDrag::motion (GdkEvent* event, bool)
3688 framecnt_t f_delta = 0;
3690 bool move_both = false;
3691 Location *real_location;
3692 Location *copy_location = 0;
3694 framepos_t const newframe = adjusted_current_frame (event);
3695 framepos_t next = newframe;
3697 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3701 CopiedLocationInfo::iterator x;
3703 /* find the marker we're dragging, and compute the delta */
3705 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3707 copy_location = (*x).location;
3709 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3711 /* this marker is represented by this
3712 * CopiedLocationMarkerInfo
3715 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3720 if (real_location->is_mark()) {
3721 f_delta = newframe - copy_location->start();
3725 switch (_marker->type()) {
3726 case Marker::SessionStart:
3727 case Marker::RangeStart:
3728 case Marker::LoopStart:
3729 case Marker::PunchIn:
3730 f_delta = newframe - copy_location->start();
3733 case Marker::SessionEnd:
3734 case Marker::RangeEnd:
3735 case Marker::LoopEnd:
3736 case Marker::PunchOut:
3737 f_delta = newframe - copy_location->end();
3740 /* what kind of marker is this ? */
3749 if (x == _copied_locations.end()) {
3750 /* hmm, impossible - we didn't find the dragged marker */
3754 /* now move them all */
3756 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3758 copy_location = x->location;
3760 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3764 if (real_location->locked()) {
3768 if (copy_location->is_mark()) {
3772 copy_location->set_start (copy_location->start() + f_delta);
3776 framepos_t new_start = copy_location->start() + f_delta;
3777 framepos_t new_end = copy_location->end() + f_delta;
3779 if (is_start) { // start-of-range marker
3781 if (move_both || (*x).move_both) {
3782 copy_location->set_start (new_start);
3783 copy_location->set_end (new_end);
3784 } else if (new_start < copy_location->end()) {
3785 copy_location->set_start (new_start);
3786 } else if (newframe > 0) {
3787 _editor->snap_to (next, RoundUpAlways, true);
3788 copy_location->set_end (next);
3789 copy_location->set_start (newframe);
3792 } else { // end marker
3794 if (move_both || (*x).move_both) {
3795 copy_location->set_end (new_end);
3796 copy_location->set_start (new_start);
3797 } else if (new_end > copy_location->start()) {
3798 copy_location->set_end (new_end);
3799 } else if (newframe > 0) {
3800 _editor->snap_to (next, RoundDownAlways, true);
3801 copy_location->set_start (next);
3802 copy_location->set_end (newframe);
3807 update_item (copy_location);
3809 /* now lookup the actual GUI items used to display this
3810 * location and move them to wherever the copy of the location
3811 * is now. This means that the logic in ARDOUR::Location is
3812 * still enforced, even though we are not (yet) modifying
3813 * the real Location itself.
3816 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3819 lm->set_position (copy_location->start(), copy_location->end());
3824 assert (!_copied_locations.empty());
3826 show_verbose_cursor_time (newframe);
3830 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3832 if (!movement_occurred) {
3834 if (was_double_click()) {
3835 _editor->rename_marker (_marker);
3839 /* just a click, do nothing but finish
3840 off the selection process
3843 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3846 case Selection::Set:
3847 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3848 _editor->selection->set (_marker);
3852 case Selection::Toggle:
3853 /* we toggle on the button release, click only */
3854 _editor->selection->toggle (_marker);
3857 case Selection::Extend:
3858 case Selection::Add:
3865 _editor->_dragging_edit_point = false;
3867 _editor->begin_reversible_command ( _("move marker") );
3868 XMLNode &before = _editor->session()->locations()->get_state();
3870 MarkerSelection::iterator i;
3871 CopiedLocationInfo::iterator x;
3874 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3875 x != _copied_locations.end() && i != _editor->selection->markers.end();
3878 Location * location = _editor->find_location_from_marker (*i, is_start);
3882 if (location->locked()) {
3886 if (location->is_mark()) {
3887 location->set_start (((*x).location)->start());
3889 location->set (((*x).location)->start(), ((*x).location)->end());
3894 XMLNode &after = _editor->session()->locations()->get_state();
3895 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3896 _editor->commit_reversible_command ();
3900 MarkerDrag::aborted (bool movement_occured)
3902 if (!movement_occured) {
3906 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3908 /* move all markers to their original location */
3911 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3914 Location * location = _editor->find_location_from_marker (*m, is_start);
3917 (*m)->set_position (is_start ? location->start() : location->end());
3924 MarkerDrag::update_item (Location*)
3929 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3931 _cumulative_x_drag (0),
3932 _cumulative_y_drag (0)
3934 if (_zero_gain_fraction < 0.0) {
3935 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3938 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3940 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3946 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3948 Drag::start_grab (event, _editor->cursors()->fader);
3950 // start the grab at the center of the control point so
3951 // the point doesn't 'jump' to the mouse after the first drag
3952 _fixed_grab_x = _point->get_x();
3953 _fixed_grab_y = _point->get_y();
3955 float const fraction = 1 - (_point->get_y() / _point->line().height());
3957 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3959 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3961 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3963 if (!_point->can_slide ()) {
3964 _x_constrained = true;
3969 ControlPointDrag::motion (GdkEvent* event, bool)
3971 double dx = _drags->current_pointer_x() - last_pointer_x();
3972 double dy = current_pointer_y() - last_pointer_y();
3974 if (event->button.state & Keyboard::SecondaryModifier) {
3979 /* coordinate in pixels relative to the start of the region (for region-based automation)
3980 or track (for track-based automation) */
3981 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3982 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3984 // calculate zero crossing point. back off by .01 to stay on the
3985 // positive side of zero
3986 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3988 // make sure we hit zero when passing through
3989 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3993 if (_x_constrained) {
3996 if (_y_constrained) {
4000 _cumulative_x_drag = cx - _fixed_grab_x;
4001 _cumulative_y_drag = cy - _fixed_grab_y;
4005 cy = min ((double) _point->line().height(), cy);
4007 framepos_t cx_frames = _editor->pixel_to_sample (cx);
4009 if (!_x_constrained) {
4010 _editor->snap_to_with_modifier (cx_frames, event);
4013 cx_frames = min (cx_frames, _point->line().maximum_time());
4015 float const fraction = 1.0 - (cy / _point->line().height());
4017 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4019 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4023 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4025 if (!movement_occurred) {
4029 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4030 _editor->reset_point_selection ();
4034 motion (event, false);
4037 _point->line().end_drag (_pushing, _final_index);
4038 _editor->commit_reversible_command ();
4042 ControlPointDrag::aborted (bool)
4044 _point->line().reset ();
4048 ControlPointDrag::active (Editing::MouseMode m)
4050 if (m == Editing::MouseDraw) {
4051 /* always active in mouse draw */
4055 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4056 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4059 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4062 _cumulative_y_drag (0)
4064 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4068 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4070 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4073 _item = &_line->grab_item ();
4075 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4076 origin, and ditto for y.
4079 double cx = event->button.x;
4080 double cy = event->button.y;
4082 _line->parent_group().canvas_to_item (cx, cy);
4084 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4089 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
4090 /* no adjacent points */
4094 Drag::start_grab (event, _editor->cursors()->fader);
4096 /* store grab start in parent frame */
4101 double fraction = 1.0 - (cy / _line->height());
4103 _line->start_drag_line (before, after, fraction);
4105 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4109 LineDrag::motion (GdkEvent* event, bool)
4111 double dy = current_pointer_y() - last_pointer_y();
4113 if (event->button.state & Keyboard::SecondaryModifier) {
4117 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4119 _cumulative_y_drag = cy - _fixed_grab_y;
4122 cy = min ((double) _line->height(), cy);
4124 double const fraction = 1.0 - (cy / _line->height());
4127 /* we are ignoring x position for this drag, so we can just pass in anything */
4128 _line->drag_motion (0, fraction, true, false, ignored);
4130 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4134 LineDrag::finished (GdkEvent* event, bool movement_occured)
4136 if (movement_occured) {
4137 motion (event, false);
4138 _line->end_drag (false, 0);
4140 /* add a new control point on the line */
4142 AutomationTimeAxisView* atv;
4144 _line->end_drag (false, 0);
4146 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4147 framepos_t where = _editor->window_event_sample (event, 0, 0);
4148 atv->add_automation_event (event, where, event->button.y, false);
4152 _editor->commit_reversible_command ();
4156 LineDrag::aborted (bool)
4161 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4164 _cumulative_x_drag (0)
4166 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4170 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4172 Drag::start_grab (event);
4174 _line = reinterpret_cast<Line*> (_item);
4177 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4179 double cx = event->button.x;
4180 double cy = event->button.y;
4182 _item->parent()->canvas_to_item (cx, cy);
4184 /* store grab start in parent frame */
4185 _region_view_grab_x = cx;
4187 _before = *(float*) _item->get_data ("position");
4189 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4191 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4195 FeatureLineDrag::motion (GdkEvent*, bool)
4197 double dx = _drags->current_pointer_x() - last_pointer_x();
4199 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4201 _cumulative_x_drag += dx;
4203 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4212 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4214 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4216 float *pos = new float;
4219 _line->set_data ("position", pos);
4225 FeatureLineDrag::finished (GdkEvent*, bool)
4227 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4228 _arv->update_transient(_before, _before);
4232 FeatureLineDrag::aborted (bool)
4237 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4239 , _vertical_only (false)
4241 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4245 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4247 Drag::start_grab (event);
4248 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4252 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4259 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4261 framepos_t grab = grab_frame ();
4262 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4263 _editor->snap_to_with_modifier (grab, event);
4265 grab = raw_grab_frame ();
4268 /* base start and end on initial click position */
4278 if (current_pointer_y() < grab_y()) {
4279 y1 = current_pointer_y();
4282 y2 = current_pointer_y();
4286 if (start != end || y1 != y2) {
4288 double x1 = _editor->sample_to_pixel (start);
4289 double x2 = _editor->sample_to_pixel (end);
4290 const double min_dimension = 2.0;
4292 if (_vertical_only) {
4293 /* fixed 10 pixel width */
4297 x2 = min (x1 - min_dimension, x2);
4299 x2 = max (x1 + min_dimension, x2);
4304 y2 = min (y1 - min_dimension, y2);
4306 y2 = max (y1 + min_dimension, y2);
4309 /* translate rect into item space and set */
4311 ArdourCanvas::Rect r (x1, y1, x2, y2);
4313 /* this drag is a _trackview_only == true drag, so the y1 and
4314 * y2 (computed using current_pointer_y() and grab_y()) will be
4315 * relative to the top of the trackview group). The
4316 * rubberband rect has the same parent/scroll offset as the
4317 * the trackview group, so we can use the "r" rect directly
4318 * to set the shape of the rubberband.
4321 _editor->rubberband_rect->set (r);
4322 _editor->rubberband_rect->show();
4323 _editor->rubberband_rect->raise_to_top();
4325 show_verbose_cursor_time (pf);
4327 do_select_things (event, true);
4332 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4336 framepos_t grab = grab_frame ();
4337 framepos_t lpf = last_pointer_frame ();
4339 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4340 grab = raw_grab_frame ();
4341 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4355 if (current_pointer_y() < grab_y()) {
4356 y1 = current_pointer_y();
4359 y2 = current_pointer_y();
4363 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4367 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4369 if (movement_occurred) {
4371 motion (event, false);
4372 do_select_things (event, false);
4378 bool do_deselect = true;
4379 MidiTimeAxisView* mtv;
4381 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4383 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4384 /* nothing selected */
4385 add_midi_region (mtv);
4386 do_deselect = false;
4390 /* do not deselect if Primary or Tertiary (toggle-select or
4391 * extend-select are pressed.
4394 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4395 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4402 _editor->rubberband_rect->hide();
4406 RubberbandSelectDrag::aborted (bool)
4408 _editor->rubberband_rect->hide ();
4411 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4412 : RegionDrag (e, i, p, v)
4414 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4418 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4420 Drag::start_grab (event, cursor);
4422 show_verbose_cursor_time (adjusted_current_frame (event));
4426 TimeFXDrag::motion (GdkEvent* event, bool)
4428 RegionView* rv = _primary;
4429 StreamView* cv = rv->get_time_axis_view().view ();
4431 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4432 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4433 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4435 framepos_t const pf = adjusted_current_frame (event);
4437 if (pf > rv->region()->position()) {
4438 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4441 show_verbose_cursor_time (pf);
4445 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4447 _primary->get_time_axis_view().hide_timestretch ();
4449 if (!movement_occurred) {
4453 if (last_pointer_frame() < _primary->region()->position()) {
4454 /* backwards drag of the left edge - not usable */
4458 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4460 float percentage = (double) newlen / (double) _primary->region()->length();
4462 #ifndef USE_RUBBERBAND
4463 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4464 if (_primary->region()->data_type() == DataType::AUDIO) {
4465 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4469 if (!_editor->get_selection().regions.empty()) {
4470 /* primary will already be included in the selection, and edit
4471 group shared editing will propagate selection across
4472 equivalent regions, so just use the current region
4476 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4477 error << _("An error occurred while executing time stretch operation") << endmsg;
4483 TimeFXDrag::aborted (bool)
4485 _primary->get_time_axis_view().hide_timestretch ();
4488 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4491 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4495 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4497 Drag::start_grab (event);
4501 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4503 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4507 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4509 if (movement_occurred && _editor->session()) {
4510 /* make sure we stop */
4511 _editor->session()->request_transport_speed (0.0);
4516 ScrubDrag::aborted (bool)
4521 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4525 , _time_selection_at_start (!_editor->get_selection().time.empty())
4527 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4529 if (_time_selection_at_start) {
4530 start_at_start = _editor->get_selection().time.start();
4531 end_at_start = _editor->get_selection().time.end_frame();
4536 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4538 if (_editor->session() == 0) {
4542 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4544 switch (_operation) {
4545 case CreateSelection:
4546 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4551 cursor = _editor->cursors()->selector;
4552 Drag::start_grab (event, cursor);
4555 case SelectionStartTrim:
4556 if (_editor->clicked_axisview) {
4557 _editor->clicked_axisview->order_selection_trims (_item, true);
4559 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4562 case SelectionEndTrim:
4563 if (_editor->clicked_axisview) {
4564 _editor->clicked_axisview->order_selection_trims (_item, false);
4566 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4570 Drag::start_grab (event, cursor);
4573 case SelectionExtend:
4574 Drag::start_grab (event, cursor);
4578 if (_operation == SelectionMove) {
4579 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4581 show_verbose_cursor_time (adjusted_current_frame (event));
4586 SelectionDrag::setup_pointer_frame_offset ()
4588 switch (_operation) {
4589 case CreateSelection:
4590 _pointer_frame_offset = 0;
4593 case SelectionStartTrim:
4595 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4598 case SelectionEndTrim:
4599 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4602 case SelectionExtend:
4608 SelectionDrag::motion (GdkEvent* event, bool first_move)
4610 framepos_t start = 0;
4612 framecnt_t length = 0;
4613 framecnt_t distance = 0;
4615 framepos_t const pending_position = adjusted_current_frame (event);
4617 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4621 switch (_operation) {
4622 case CreateSelection:
4624 framepos_t grab = grab_frame ();
4627 grab = adjusted_current_frame (event, false);
4628 if (grab < pending_position) {
4629 _editor->snap_to (grab, RoundDownMaybe);
4631 _editor->snap_to (grab, RoundUpMaybe);
4635 if (pending_position < grab) {
4636 start = pending_position;
4639 end = pending_position;
4643 /* first drag: Either add to the selection
4644 or create a new selection
4651 /* adding to the selection */
4652 _editor->set_selected_track_as_side_effect (Selection::Add);
4653 _editor->clicked_selection = _editor->selection->add (start, end);
4660 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4661 _editor->set_selected_track_as_side_effect (Selection::Set);
4664 _editor->clicked_selection = _editor->selection->set (start, end);
4668 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4669 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4670 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4672 _editor->selection->add (atest);
4676 /* select all tracks within the rectangle that we've marked out so far */
4677 TrackViewList new_selection;
4678 TrackViewList& all_tracks (_editor->track_views);
4680 ArdourCanvas::Coord const top = grab_y();
4681 ArdourCanvas::Coord const bottom = current_pointer_y();
4683 if (top >= 0 && bottom >= 0) {
4685 //first, find the tracks that are covered in the y range selection
4686 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4687 if ((*i)->covered_by_y_range (top, bottom)) {
4688 new_selection.push_back (*i);
4692 //now find any tracks that are GROUPED with the tracks we selected
4693 TrackViewList grouped_add = new_selection;
4694 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4695 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4696 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4697 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4698 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4699 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4700 grouped_add.push_back (*j);
4705 //now compare our list with the current selection, and add or remove as necessary
4706 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4707 TrackViewList tracks_to_add;
4708 TrackViewList tracks_to_remove;
4709 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4710 if ( !_editor->selection->tracks.contains ( *i ) )
4711 tracks_to_add.push_back ( *i );
4712 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4713 if ( !grouped_add.contains ( *i ) )
4714 tracks_to_remove.push_back ( *i );
4715 _editor->selection->add(tracks_to_add);
4716 _editor->selection->remove(tracks_to_remove);
4722 case SelectionStartTrim:
4724 start = _editor->selection->time[_editor->clicked_selection].start;
4725 end = _editor->selection->time[_editor->clicked_selection].end;
4727 if (pending_position > end) {
4730 start = pending_position;
4734 case SelectionEndTrim:
4736 start = _editor->selection->time[_editor->clicked_selection].start;
4737 end = _editor->selection->time[_editor->clicked_selection].end;
4739 if (pending_position < start) {
4742 end = pending_position;
4749 start = _editor->selection->time[_editor->clicked_selection].start;
4750 end = _editor->selection->time[_editor->clicked_selection].end;
4752 length = end - start;
4753 distance = pending_position - start;
4754 start = pending_position;
4755 _editor->snap_to (start);
4757 end = start + length;
4761 case SelectionExtend:
4766 switch (_operation) {
4768 if (_time_selection_at_start) {
4769 _editor->selection->move_time (distance);
4773 _editor->selection->replace (_editor->clicked_selection, start, end);
4777 if (_operation == SelectionMove) {
4778 show_verbose_cursor_time(start);
4780 show_verbose_cursor_time(pending_position);
4785 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4787 Session* s = _editor->session();
4789 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4790 if (movement_occurred) {
4791 motion (event, false);
4792 /* XXX this is not object-oriented programming at all. ick */
4793 if (_editor->selection->time.consolidate()) {
4794 _editor->selection->TimeChanged ();
4797 /* XXX what if its a music time selection? */
4799 if ( s->get_play_range() && s->transport_rolling() ) {
4800 s->request_play_range (&_editor->selection->time, true);
4802 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4803 if (_operation == SelectionEndTrim)
4804 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4806 s->request_locate (_editor->get_selection().time.start());
4812 /* just a click, no pointer movement.
4815 if (_operation == SelectionExtend) {
4816 if (_time_selection_at_start) {
4817 framepos_t pos = adjusted_current_frame (event, false);
4818 framepos_t start = min (pos, start_at_start);
4819 framepos_t end = max (pos, end_at_start);
4820 _editor->selection->set (start, end);
4823 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4824 if (_editor->clicked_selection) {
4825 _editor->selection->remove (_editor->clicked_selection);
4828 if (!_editor->clicked_selection) {
4829 _editor->selection->clear_time();
4834 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4835 _editor->selection->set (_editor->clicked_axisview);
4838 if (s && s->get_play_range () && s->transport_rolling()) {
4839 s->request_stop (false, false);
4844 _editor->stop_canvas_autoscroll ();
4845 _editor->clicked_selection = 0;
4846 _editor->commit_reversible_selection_op ();
4850 SelectionDrag::aborted (bool)
4855 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4856 : Drag (e, i, false),
4860 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4862 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4863 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4864 physical_screen_height (_editor->get_window())));
4865 _drag_rect->hide ();
4867 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4868 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4871 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4873 /* normal canvas items will be cleaned up when their parent group is deleted. But
4874 this item is created as the child of a long-lived parent group, and so we
4875 need to explicitly delete it.
4881 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4883 if (_editor->session() == 0) {
4887 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4889 if (!_editor->temp_location) {
4890 _editor->temp_location = new Location (*_editor->session());
4893 switch (_operation) {
4894 case CreateSkipMarker:
4895 case CreateRangeMarker:
4896 case CreateTransportMarker:
4897 case CreateCDMarker:
4899 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4904 cursor = _editor->cursors()->selector;
4908 Drag::start_grab (event, cursor);
4910 show_verbose_cursor_time (adjusted_current_frame (event));
4914 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4916 framepos_t start = 0;
4918 ArdourCanvas::Rectangle *crect;
4920 switch (_operation) {
4921 case CreateSkipMarker:
4922 crect = _editor->range_bar_drag_rect;
4924 case CreateRangeMarker:
4925 crect = _editor->range_bar_drag_rect;
4927 case CreateTransportMarker:
4928 crect = _editor->transport_bar_drag_rect;
4930 case CreateCDMarker:
4931 crect = _editor->cd_marker_bar_drag_rect;
4934 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4939 framepos_t const pf = adjusted_current_frame (event);
4941 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4942 framepos_t grab = grab_frame ();
4943 _editor->snap_to (grab);
4945 if (pf < grab_frame()) {
4953 /* first drag: Either add to the selection
4954 or create a new selection.
4959 _editor->temp_location->set (start, end);
4963 update_item (_editor->temp_location);
4965 //_drag_rect->raise_to_top();
4971 _editor->temp_location->set (start, end);
4973 double x1 = _editor->sample_to_pixel (start);
4974 double x2 = _editor->sample_to_pixel (end);
4978 update_item (_editor->temp_location);
4981 show_verbose_cursor_time (pf);
4986 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4988 Location * newloc = 0;
4992 if (movement_occurred) {
4993 motion (event, false);
4996 switch (_operation) {
4997 case CreateSkipMarker:
4998 case CreateRangeMarker:
4999 case CreateCDMarker:
5001 XMLNode &before = _editor->session()->locations()->get_state();
5002 if (_operation == CreateSkipMarker) {
5003 _editor->begin_reversible_command (_("new skip marker"));
5004 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5005 flags = Location::IsRangeMarker | Location::IsSkip;
5006 _editor->range_bar_drag_rect->hide();
5007 } else if (_operation == CreateCDMarker) {
5008 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5009 _editor->begin_reversible_command (_("new CD marker"));
5010 flags = Location::IsRangeMarker | Location::IsCDMarker;
5011 _editor->cd_marker_bar_drag_rect->hide();
5013 _editor->begin_reversible_command (_("new skip marker"));
5014 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5015 flags = Location::IsRangeMarker;
5016 _editor->range_bar_drag_rect->hide();
5018 newloc = new Location (
5019 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5022 _editor->session()->locations()->add (newloc, true);
5023 XMLNode &after = _editor->session()->locations()->get_state();
5024 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5025 _editor->commit_reversible_command ();
5029 case CreateTransportMarker:
5030 // popup menu to pick loop or punch
5031 _editor->new_transport_marker_context_menu (&event->button, _item);
5037 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5039 if (_operation == CreateTransportMarker) {
5041 /* didn't drag, so just locate */
5043 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5045 } else if (_operation == CreateCDMarker) {
5047 /* didn't drag, but mark is already created so do
5050 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5055 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5057 if (end == max_framepos) {
5058 end = _editor->session()->current_end_frame ();
5061 if (start == max_framepos) {
5062 start = _editor->session()->current_start_frame ();
5065 switch (_editor->mouse_mode) {
5067 /* find the two markers on either side and then make the selection from it */
5068 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5072 /* find the two markers on either side of the click and make the range out of it */
5073 _editor->selection->set (start, end);
5082 _editor->stop_canvas_autoscroll ();
5086 RangeMarkerBarDrag::aborted (bool movement_occured)
5088 if (movement_occured) {
5089 _drag_rect->hide ();
5094 RangeMarkerBarDrag::update_item (Location* location)
5096 double const x1 = _editor->sample_to_pixel (location->start());
5097 double const x2 = _editor->sample_to_pixel (location->end());
5099 _drag_rect->set_x0 (x1);
5100 _drag_rect->set_x1 (x2);
5103 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5105 , _cumulative_dx (0)
5106 , _cumulative_dy (0)
5108 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5110 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5112 _region = &_primary->region_view ();
5113 _note_height = _region->midi_stream_view()->note_height ();
5117 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5119 Drag::start_grab (event);
5121 if (!(_was_selected = _primary->selected())) {
5123 /* tertiary-click means extend selection - we'll do that on button release,
5124 so don't add it here, because otherwise we make it hard to figure
5125 out the "extend-to" range.
5128 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5131 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5134 _region->note_selected (_primary, true);
5136 _region->unique_select (_primary);
5139 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5140 _editor->commit_reversible_selection_op();
5145 /** @return Current total drag x change in frames */
5147 NoteDrag::total_dx () const
5150 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5152 /* primary note time */
5153 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5155 /* new time of the primary note in session frames */
5156 frameoffset_t st = n + dx;
5158 framepos_t const rp = _region->region()->position ();
5160 /* prevent the note being dragged earlier than the region's position */
5163 /* snap and return corresponding delta */
5164 return _region->snap_frame_to_frame (st - rp) + rp - n;
5167 /** @return Current total drag y change in note number */
5169 NoteDrag::total_dy () const
5171 MidiStreamView* msv = _region->midi_stream_view ();
5172 double const y = _region->midi_view()->y_position ();
5173 /* new current note */
5174 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5176 n = max (msv->lowest_note(), n);
5177 n = min (msv->highest_note(), n);
5178 /* and work out delta */
5179 return n - msv->y_to_note (grab_y() - y);
5183 NoteDrag::motion (GdkEvent *, bool)
5185 /* Total change in x and y since the start of the drag */
5186 frameoffset_t const dx = total_dx ();
5187 int8_t const dy = total_dy ();
5189 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5190 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5191 double const tdy = -dy * _note_height - _cumulative_dy;
5194 _cumulative_dx += tdx;
5195 _cumulative_dy += tdy;
5197 int8_t note_delta = total_dy();
5199 _region->move_selection (tdx, tdy, note_delta);
5201 /* the new note value may be the same as the old one, but we
5202 * don't know what that means because the selection may have
5203 * involved more than one note and we might be doing something
5204 * odd with them. so show the note value anyway, always.
5208 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5210 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5211 (int) floor ((double)new_note));
5213 show_verbose_cursor_text (buf);
5218 NoteDrag::finished (GdkEvent* ev, bool moved)
5221 /* no motion - select note */
5223 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5224 _editor->current_mouse_mode() == Editing::MouseDraw) {
5226 bool changed = false;
5228 if (_was_selected) {
5229 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5231 _region->note_deselected (_primary);
5235 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5236 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5238 if (!extend && !add && _region->selection_size() > 1) {
5239 _region->unique_select (_primary);
5241 } else if (extend) {
5242 _region->note_selected (_primary, true, true);
5245 /* it was added during button press */
5250 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5251 _editor->commit_reversible_selection_op();
5255 _region->note_dropped (_primary, total_dx(), total_dy());
5260 NoteDrag::aborted (bool)
5265 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5266 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5267 : Drag (editor, atv->base_item ())
5269 , _y_origin (atv->y_position())
5270 , _nothing_to_drag (false)
5272 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5273 setup (atv->lines ());
5276 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5277 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5278 : Drag (editor, rv->get_canvas_group ())
5280 , _y_origin (rv->get_time_axis_view().y_position())
5281 , _nothing_to_drag (false)
5284 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5286 list<boost::shared_ptr<AutomationLine> > lines;
5288 AudioRegionView* audio_view;
5289 AutomationRegionView* automation_view;
5290 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5291 lines.push_back (audio_view->get_gain_line ());
5292 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5293 lines.push_back (automation_view->line ());
5296 error << _("Automation range drag created for invalid region type") << endmsg;
5302 /** @param lines AutomationLines to drag.
5303 * @param offset Offset from the session start to the points in the AutomationLines.
5306 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5308 /* find the lines that overlap the ranges being dragged */
5309 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5310 while (i != lines.end ()) {
5311 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5314 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5316 /* check this range against all the AudioRanges that we are using */
5317 list<AudioRange>::const_iterator k = _ranges.begin ();
5318 while (k != _ranges.end()) {
5319 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5325 /* add it to our list if it overlaps at all */
5326 if (k != _ranges.end()) {
5331 _lines.push_back (n);
5337 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5341 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5343 return 1.0 - ((global_y - _y_origin) / line->height());
5347 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5349 const double v = list->eval(x);
5350 return _integral ? rint(v) : v;
5354 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5356 Drag::start_grab (event, cursor);
5358 /* Get line states before we start changing things */
5359 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5360 i->state = &i->line->get_state ();
5361 i->original_fraction = y_fraction (i->line, current_pointer_y());
5364 if (_ranges.empty()) {
5366 /* No selected time ranges: drag all points */
5367 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5368 uint32_t const N = i->line->npoints ();
5369 for (uint32_t j = 0; j < N; ++j) {
5370 i->points.push_back (i->line->nth (j));
5376 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5378 framecnt_t const half = (i->start + i->end) / 2;
5380 /* find the line that this audio range starts in */
5381 list<Line>::iterator j = _lines.begin();
5382 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5386 if (j != _lines.end()) {
5387 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5389 /* j is the line that this audio range starts in; fade into it;
5390 64 samples length plucked out of thin air.
5393 framepos_t a = i->start + 64;
5398 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5399 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5401 the_list->editor_add (p, value (the_list, p));
5402 the_list->editor_add (q, value (the_list, q));
5405 /* same thing for the end */
5408 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5412 if (j != _lines.end()) {
5413 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5415 /* j is the line that this audio range starts in; fade out of it;
5416 64 samples length plucked out of thin air.
5419 framepos_t b = i->end - 64;
5424 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5425 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5427 the_list->editor_add (p, value (the_list, p));
5428 the_list->editor_add (q, value (the_list, q));
5432 _nothing_to_drag = true;
5434 /* Find all the points that should be dragged and put them in the relevant
5435 points lists in the Line structs.
5438 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5440 uint32_t const N = i->line->npoints ();
5441 for (uint32_t j = 0; j < N; ++j) {
5443 /* here's a control point on this line */
5444 ControlPoint* p = i->line->nth (j);
5445 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5447 /* see if it's inside a range */
5448 list<AudioRange>::const_iterator k = _ranges.begin ();
5449 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5453 if (k != _ranges.end()) {
5454 /* dragging this point */
5455 _nothing_to_drag = false;
5456 i->points.push_back (p);
5462 if (_nothing_to_drag) {
5466 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5467 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5472 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5474 if (_nothing_to_drag) {
5478 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5479 float const f = y_fraction (l->line, current_pointer_y());
5480 /* we are ignoring x position for this drag, so we can just pass in anything */
5482 l->line->drag_motion (0, f, true, false, ignored);
5483 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5488 AutomationRangeDrag::finished (GdkEvent* event, bool)
5490 if (_nothing_to_drag) {
5494 motion (event, false);
5495 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5496 i->line->end_drag (false, 0);
5499 _editor->commit_reversible_command ();
5503 AutomationRangeDrag::aborted (bool)
5505 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5510 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5512 , initial_time_axis_view (itav)
5514 /* note that time_axis_view may be null if the regionview was created
5515 * as part of a copy operation.
5517 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5518 layer = v->region()->layer ();
5519 initial_y = v->get_canvas_group()->position().y;
5520 initial_playlist = v->region()->playlist ();
5521 initial_position = v->region()->position ();
5522 initial_end = v->region()->position () + v->region()->length ();
5525 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5526 : Drag (e, i->canvas_item ())
5529 , _cumulative_dx (0)
5531 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5532 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5537 PatchChangeDrag::motion (GdkEvent* ev, bool)
5539 framepos_t f = adjusted_current_frame (ev);
5540 boost::shared_ptr<Region> r = _region_view->region ();
5541 f = max (f, r->position ());
5542 f = min (f, r->last_frame ());
5544 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5545 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5546 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5547 _cumulative_dx = dxu;
5551 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5553 if (!movement_occurred) {
5557 boost::shared_ptr<Region> r (_region_view->region ());
5558 framepos_t f = adjusted_current_frame (ev);
5559 f = max (f, r->position ());
5560 f = min (f, r->last_frame ());
5562 _region_view->move_patch_change (
5564 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5569 PatchChangeDrag::aborted (bool)
5571 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5575 PatchChangeDrag::setup_pointer_frame_offset ()
5577 boost::shared_ptr<Region> region = _region_view->region ();
5578 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5581 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5582 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5589 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5591 _region_view->update_drag_selection (
5593 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5597 MidiRubberbandSelectDrag::deselect_things ()
5602 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5603 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5606 _vertical_only = true;
5610 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5612 double const y = _region_view->midi_view()->y_position ();
5614 y1 = max (0.0, y1 - y);
5615 y2 = max (0.0, y2 - y);
5617 _region_view->update_vertical_drag_selection (
5620 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5625 MidiVerticalSelectDrag::deselect_things ()
5630 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5631 : RubberbandSelectDrag (e, i)
5637 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5639 if (drag_in_progress) {
5640 /* We just want to select things at the end of the drag, not during it */
5644 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5646 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5648 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5650 _editor->commit_reversible_selection_op ();
5654 EditorRubberbandSelectDrag::deselect_things ()
5656 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5658 _editor->selection->clear_tracks();
5659 _editor->selection->clear_regions();
5660 _editor->selection->clear_points ();
5661 _editor->selection->clear_lines ();
5662 _editor->selection->clear_midi_notes ();
5664 _editor->commit_reversible_selection_op();
5667 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5672 _note[0] = _note[1] = 0;
5675 NoteCreateDrag::~NoteCreateDrag ()
5681 NoteCreateDrag::grid_frames (framepos_t t) const
5684 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5686 grid_beats = Evoral::Beats(1);
5689 return _region_view->region_beats_to_region_frames (grid_beats);
5693 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5695 Drag::start_grab (event, cursor);
5697 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5699 framepos_t pf = _drags->current_pointer_frame ();
5700 framecnt_t const g = grid_frames (pf);
5702 /* Hack so that we always snap to the note that we are over, instead of snapping
5703 to the next one if we're more than halfway through the one we're over.
5705 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5709 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5710 _note[1] = _note[0];
5712 MidiStreamView* sv = _region_view->midi_stream_view ();
5713 double const x = _editor->sample_to_pixel (_note[0]);
5714 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5716 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5717 _drag_rect->set_outline_all ();
5718 _drag_rect->set_outline_color (0xffffff99);
5719 _drag_rect->set_fill_color (0xffffff66);
5723 NoteCreateDrag::motion (GdkEvent* event, bool)
5725 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5726 double const x0 = _editor->sample_to_pixel (_note[0]);
5727 double const x1 = _editor->sample_to_pixel (_note[1]);
5728 _drag_rect->set_x0 (std::min(x0, x1));
5729 _drag_rect->set_x1 (std::max(x0, x1));
5733 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5735 if (!had_movement) {
5739 framepos_t const start = min (_note[0], _note[1]);
5740 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5742 framecnt_t const g = grid_frames (start);
5743 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5745 if (_editor->snap_mode() == SnapNormal && length < g) {
5749 Evoral::Beats length_beats = max (
5750 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5752 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5756 NoteCreateDrag::y_to_region (double y) const
5759 _region_view->get_canvas_group()->canvas_to_item (x, y);
5764 NoteCreateDrag::aborted (bool)
5769 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5774 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5778 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5780 Drag::start_grab (event, cursor);
5784 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5790 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5793 distance = _drags->current_pointer_x() - grab_x();
5794 len = ar->fade_in()->back()->when;
5796 distance = grab_x() - _drags->current_pointer_x();
5797 len = ar->fade_out()->back()->when;
5800 /* how long should it be ? */
5802 new_length = len + _editor->pixel_to_sample (distance);
5804 /* now check with the region that this is legal */
5806 new_length = ar->verify_xfade_bounds (new_length, start);
5809 arv->reset_fade_in_shape_width (ar, new_length);
5811 arv->reset_fade_out_shape_width (ar, new_length);
5816 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5822 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5825 distance = _drags->current_pointer_x() - grab_x();
5826 len = ar->fade_in()->back()->when;
5828 distance = grab_x() - _drags->current_pointer_x();
5829 len = ar->fade_out()->back()->when;
5832 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5834 _editor->begin_reversible_command ("xfade trim");
5835 ar->playlist()->clear_owned_changes ();
5838 ar->set_fade_in_length (new_length);
5840 ar->set_fade_out_length (new_length);
5843 /* Adjusting the xfade may affect other regions in the playlist, so we need
5844 to get undo Commands from the whole playlist rather than just the
5848 vector<Command*> cmds;
5849 ar->playlist()->rdiff (cmds);
5850 _editor->session()->add_commands (cmds);
5851 _editor->commit_reversible_command ();
5856 CrossfadeEdgeDrag::aborted (bool)
5859 // arv->redraw_start_xfade ();
5861 // arv->redraw_end_xfade ();
5865 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5866 : Drag (e, item, true)
5867 , line (new EditorCursor (*e))
5869 line->set_position (pos);
5873 RegionCutDrag::~RegionCutDrag ()
5879 RegionCutDrag::motion (GdkEvent*, bool)
5881 framepos_t where = _drags->current_pointer_frame();
5882 _editor->snap_to (where);
5884 line->set_position (where);
5888 RegionCutDrag::finished (GdkEvent*, bool)
5890 _editor->get_track_canvas()->canvas()->re_enter();
5892 framepos_t pos = _drags->current_pointer_frame();
5896 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5902 _editor->split_regions_at (pos, rs);
5906 RegionCutDrag::aborted (bool)