2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
49 #include "audio_region_view.h"
50 #include "automation_region_view.h"
51 #include "midi_region_view.h"
52 #include "ardour_ui.h"
53 #include "gui_thread.h"
54 #include "control_point.h"
55 #include "region_gain_line.h"
56 #include "editor_drag.h"
57 #include "audio_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "selection.h"
60 #include "midi_selection.h"
61 #include "automation_time_axis.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "verbose_cursor.h"
70 using namespace ARDOUR;
73 using namespace Gtkmm2ext;
74 using namespace Editing;
75 using namespace ArdourCanvas;
77 using Gtkmm2ext::Keyboard;
79 double ControlPointDrag::_zero_gain_fraction = -1.0;
81 DragManager::DragManager (Editor* e)
84 , _current_pointer_frame (0)
88 DragManager::~DragManager ()
93 /** Call abort for each active drag */
99 cerr << "Aborting drag\n";
101 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
106 if (!_drags.empty ()) {
107 _editor->set_follow_playhead (_old_follow_playhead, false);
111 _editor->abort_reversible_command();
117 DragManager::add (Drag* d)
119 d->set_manager (this);
120 _drags.push_back (d);
124 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
126 d->set_manager (this);
127 _drags.push_back (d);
132 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
134 /* Prevent follow playhead during the drag to be nice to the user */
135 _old_follow_playhead = _editor->follow_playhead ();
136 _editor->set_follow_playhead (false);
138 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
140 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
141 (*i)->start_grab (e, c);
145 /** Call end_grab for each active drag.
146 * @return true if any drag reported movement having occurred.
149 DragManager::end_grab (GdkEvent* e)
154 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
155 bool const t = (*i)->end_grab (e);
166 _editor->set_follow_playhead (_old_follow_playhead, false);
172 DragManager::mark_double_click ()
174 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
175 (*i)->set_double_click (true);
180 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
184 /* calling this implies that we expect the event to have canvas
187 * Can we guarantee that this is true?
190 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
192 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
193 bool const t = (*i)->motion_handler (e, from_autoscroll);
194 /* run all handlers; return true if at least one of them
195 returns true (indicating that the event has been handled).
207 DragManager::have_item (ArdourCanvas::Item* i) const
209 list<Drag*>::const_iterator j = _drags.begin ();
210 while (j != _drags.end() && (*j)->item () != i) {
214 return j != _drags.end ();
217 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
220 , _pointer_frame_offset (0)
221 , _trackview_only (trackview_only)
222 , _move_threshold_passed (false)
223 , _starting_point_passed (false)
224 , _initially_vertical (false)
225 , _was_double_click (false)
226 , _raw_grab_frame (0)
228 , _last_pointer_frame (0)
234 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
240 _cursor_ctx = CursorContext::create (*_editor, cursor);
242 _cursor_ctx->change (cursor);
249 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
251 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
253 if (Keyboard::is_button2_event (&event->button)) {
254 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
255 _y_constrained = true;
256 _x_constrained = false;
258 _y_constrained = false;
259 _x_constrained = true;
262 _x_constrained = false;
263 _y_constrained = false;
266 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
267 setup_pointer_frame_offset ();
268 _grab_frame = adjusted_frame (_raw_grab_frame, event);
269 _last_pointer_frame = _grab_frame;
270 _last_pointer_x = _grab_x;
272 if (_trackview_only) {
273 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
276 _last_pointer_y = _grab_y;
280 if (!_editor->cursors()->is_invalid (cursor)) {
281 /* CAIROCANVAS need a variant here that passes *cursor */
282 _cursor_ctx = CursorContext::create (*_editor, cursor);
285 if (_editor->session() && _editor->session()->transport_rolling()) {
288 _was_rolling = false;
291 switch (_editor->snap_type()) {
292 case SnapToRegionStart:
293 case SnapToRegionEnd:
294 case SnapToRegionSync:
295 case SnapToRegionBoundary:
296 _editor->build_region_boundary_cache ();
303 /** Call to end a drag `successfully'. Ungrabs item and calls
304 * subclass' finished() method.
306 * @param event GDK event, or 0.
307 * @return true if some movement occurred, otherwise false.
310 Drag::end_grab (GdkEvent* event)
312 _editor->stop_canvas_autoscroll ();
316 finished (event, _move_threshold_passed);
318 _editor->verbose_cursor()->hide ();
321 return _move_threshold_passed;
325 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
329 if (f > _pointer_frame_offset) {
330 pos = f - _pointer_frame_offset;
334 _editor->snap_to_with_modifier (pos, event);
341 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
343 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
347 Drag::current_pointer_x() const
349 return _drags->current_pointer_x ();
353 Drag::current_pointer_y () const
355 if (!_trackview_only) {
356 return _drags->current_pointer_y ();
359 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
363 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
365 /* check to see if we have moved in any way that matters since the last motion event */
366 if (_move_threshold_passed &&
367 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
368 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
372 pair<framecnt_t, int> const threshold = move_threshold ();
374 bool const old_move_threshold_passed = _move_threshold_passed;
376 if (!_move_threshold_passed) {
378 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
379 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
381 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
384 if (active (_editor->mouse_mode) && _move_threshold_passed) {
386 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
388 if (old_move_threshold_passed != _move_threshold_passed) {
392 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
393 _initially_vertical = true;
395 _initially_vertical = false;
399 if (!from_autoscroll) {
400 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
403 if (!_editor->autoscroll_active() || from_autoscroll) {
406 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
408 motion (event, first_move && !_starting_point_passed);
410 if (first_move && !_starting_point_passed) {
411 _starting_point_passed = true;
414 _last_pointer_x = _drags->current_pointer_x ();
415 _last_pointer_y = current_pointer_y ();
416 _last_pointer_frame = adjusted_current_frame (event);
426 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
434 aborted (_move_threshold_passed);
436 _editor->stop_canvas_autoscroll ();
437 _editor->verbose_cursor()->hide ();
441 Drag::show_verbose_cursor_time (framepos_t frame)
443 _editor->verbose_cursor()->set_time (frame);
444 _editor->verbose_cursor()->show ();
448 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
450 _editor->verbose_cursor()->set_duration (start, end);
451 _editor->verbose_cursor()->show ();
455 Drag::show_verbose_cursor_text (string const & text)
457 _editor->verbose_cursor()->set (text);
458 _editor->verbose_cursor()->show ();
461 boost::shared_ptr<Region>
462 Drag::add_midi_region (MidiTimeAxisView* view)
464 if (_editor->session()) {
465 const TempoMap& map (_editor->session()->tempo_map());
466 framecnt_t pos = grab_frame();
467 const Meter& m = map.meter_at (pos);
468 /* not that the frame rate used here can be affected by pull up/down which
471 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
472 return view->add_region (grab_frame(), len, true);
475 return boost::shared_ptr<Region>();
478 struct EditorOrderTimeAxisViewSorter {
479 bool operator() (TimeAxisView* a, TimeAxisView* b) {
480 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
481 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
483 return ra->route()->order_key () < rb->route()->order_key ();
487 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
492 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
494 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
495 as some of the regions we are dragging may be on such tracks.
498 TrackViewList track_views = _editor->track_views;
499 track_views.sort (EditorOrderTimeAxisViewSorter ());
501 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
502 _time_axis_views.push_back (*i);
504 TimeAxisView::Children children_list = (*i)->get_child_list ();
505 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
506 _time_axis_views.push_back (j->get());
510 /* the list of views can be empty at this point if this is a region list-insert drag
513 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
514 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
517 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
521 RegionDrag::region_going_away (RegionView* v)
523 list<DraggingView>::iterator i = _views.begin ();
524 while (i != _views.end() && i->view != v) {
528 if (i != _views.end()) {
533 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
534 * or -1 if it is not found.
537 RegionDrag::find_time_axis_view (TimeAxisView* t) const
540 int const N = _time_axis_views.size ();
541 while (i < N && _time_axis_views[i] != t) {
545 if (_time_axis_views[i] != t) {
552 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
553 : RegionDrag (e, i, p, v)
556 , _last_pointer_time_axis_view (0)
557 , _last_pointer_layer (0)
558 , _single_axis (false)
563 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
567 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
569 Drag::start_grab (event, cursor);
571 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
575 show_verbose_cursor_time (_last_frame_position);
577 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
579 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
580 assert(_last_pointer_time_axis_view >= 0);
581 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
586 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
588 /* compute the amount of pointer motion in frames, and where
589 the region would be if we moved it by that much.
591 *pending_region_position = adjusted_current_frame (event);
593 framepos_t sync_frame;
594 framecnt_t sync_offset;
597 sync_offset = _primary->region()->sync_offset (sync_dir);
599 /* we don't handle a sync point that lies before zero.
601 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
603 sync_frame = *pending_region_position + (sync_dir*sync_offset);
605 _editor->snap_to_with_modifier (sync_frame, event);
607 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
610 *pending_region_position = _last_frame_position;
613 if (*pending_region_position > max_framepos - _primary->region()->length()) {
614 *pending_region_position = _last_frame_position;
619 /* in locked edit mode, reverse the usual meaning of _x_constrained */
620 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
622 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
624 /* x movement since last time (in pixels) */
625 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
627 /* total x movement */
628 framecnt_t total_dx = *pending_region_position;
629 if (regions_came_from_canvas()) {
630 total_dx = total_dx - grab_frame ();
633 /* check that no regions have gone off the start of the session */
634 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
635 if ((i->view->region()->position() + total_dx) < 0) {
637 *pending_region_position = _last_frame_position;
648 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
654 const int tavsize = _time_axis_views.size();
655 const int dt = delta > 0 ? +1 : -1;
657 int target = start + delta - skip;
659 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
660 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
662 while (current >= 0 && current != target) {
664 if (current < 0 && dt < 0) {
667 if (current >= tavsize && dt > 0) {
670 if (current < 0 || current >= tavsize) {
674 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
675 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
679 if (distance_only && current == start + delta) {
687 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
689 if (_y_constrained) {
693 const int tavsize = _time_axis_views.size();
694 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
695 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
696 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
698 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
699 /* already in the drop zone */
700 if (delta_track >= 0) {
701 /* downward motion - OK if others are still not in the dropzone */
710 } else if (n >= tavsize) {
711 /* downward motion into drop zone. That's fine. */
715 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
716 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
717 /* not a track, or the wrong type */
721 double const l = i->layer + delta_layer;
723 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
724 mode to allow the user to place a region below another on layer 0.
726 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
727 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
728 If it has, the layers will be munged later anyway, so it's ok.
734 /* all regions being dragged are ok with this change */
738 struct DraggingViewSorter {
739 bool operator() (const DraggingView& a, const DraggingView& b) {
740 return a.time_axis_view < b.time_axis_view;
745 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
747 double delta_layer = 0;
748 int delta_time_axis_view = 0;
749 int current_pointer_time_axis_view = -1;
751 assert (!_views.empty ());
755 if (initially_vertical()) {
756 _y_constrained = false;
757 _x_constrained = true;
759 _y_constrained = true;
760 _x_constrained = false;
765 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
767 /* Find the TimeAxisView that the pointer is now over */
768 const double cur_y = current_pointer_y ();
769 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
770 TimeAxisView* tv = r.first;
772 if (!tv && cur_y < 0) {
773 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
777 /* find drop-zone y-position */
778 Coord last_track_bottom_edge;
779 last_track_bottom_edge = 0;
780 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
781 if (!(*t)->hidden()) {
782 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
787 if (tv && tv->view()) {
788 /* the mouse is over a track */
789 double layer = r.second;
791 if (first_move && tv->view()->layer_display() == Stacked) {
792 tv->view()->set_layer_display (Expanded);
795 /* Here's the current pointer position in terms of time axis view and layer */
796 current_pointer_time_axis_view = find_time_axis_view (tv);
797 assert(current_pointer_time_axis_view >= 0);
799 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
801 /* Work out the change in y */
803 if (_last_pointer_time_axis_view < 0) {
804 /* Was in the drop-zone, now over a track.
805 * Hence it must be an upward move (from the bottom)
807 * track_index is still -1, so delta must be set to
808 * move up the correct number of tracks from the bottom.
810 * This is necessary because steps may be skipped if
811 * the bottom-most track is not a valid target and/or
812 * if there are hidden tracks at the bottom.
813 * Hence the initial offset (_ddropzone) as well as the
814 * last valid pointer position (_pdropzone) need to be
815 * taken into account.
817 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
819 /* ignore busses early on. we can't move any regions on them */
820 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
821 if (rtv && rtv->is_track()) {
822 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
826 /* TODO needs adjustment per DraggingView,
828 * e.g. select one region on the top-layer of a track
829 * and one region which is at the bottom-layer of another track
832 * Indicated drop-zones and layering is wrong.
833 * and may infer additional layers on the target-track
834 * (depending how many layers the original track had).
836 * Or select two regions (different layers) on a same track,
837 * move across a non-layer track.. -> layering info is lost.
838 * on drop either of the regions may be on top.
840 * Proposed solution: screw it :) well,
841 * don't use delta_layer, use an absolute value
842 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
843 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
844 * 3) iterate over all DraggingView, find the one that is over the track with most layers
845 * 4) proportionally scale layer to layers available on target
847 delta_layer = current_pointer_layer - _last_pointer_layer;
850 /* for automation lanes, there is a TimeAxisView but no ->view()
851 * if (!tv) -> dropzone
853 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
854 /* Moving into the drop-zone.. */
855 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
856 /* delta_time_axis_view may not be sufficient to move into the DZ
857 * the mouse may enter it, but it may not be a valid move due to
860 * -> remember the delta needed to move into the dropzone
862 _ddropzone = delta_time_axis_view;
863 /* ..but subtract hidden tracks (or routes) at the bottom.
864 * we silently move mover them
866 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
867 - _time_axis_views.size();
869 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
870 /* move around inside the zone.
871 * This allows to move further down until all regions are in the zone.
873 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
874 assert(ptr_y >= last_track_bottom_edge);
875 assert(_ddropzone > 0);
877 /* calculate mouse position in 'tracks' below last track. */
878 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
879 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
881 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
883 delta_time_axis_view = dzpos - _pdropzone;
884 } else if (dzpos < _pdropzone && _ndropzone > 0) {
885 // move up inside the DZ
886 delta_time_axis_view = dzpos - _pdropzone;
890 /* Work out the change in x */
891 framepos_t pending_region_position;
892 double const x_delta = compute_x_delta (event, &pending_region_position);
893 _last_frame_position = pending_region_position;
895 /* calculate hidden tracks in current y-axis delta */
897 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
898 /* The mouse is more than one track below the dropzone.
899 * distance calculation is not needed (and would not work, either
900 * because the dropzone is "packed").
902 * Except when partially(!) moving regions out of dropzone in a large step.
903 * (the mouse may or may not remain in the DZ)
904 * Hidden tracks at the bottom of the TAV need to be skipped.
906 assert(_pdropzone >= _ddropzone);
907 if (delta_time_axis_view < 0 && -delta_time_axis_view >= _pdropzone - _ddropzone)
909 const int dt = delta_time_axis_view + _pdropzone - _ddropzone;
911 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
912 -_time_axis_views.size() - dt;
915 else if (_last_pointer_time_axis_view < 0) {
916 /* Moving out of the zone. Check for hidden tracks at the bottom. */
917 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
918 -_time_axis_views.size() - delta_time_axis_view;
920 /* calculate hidden tracks that are skipped by the pointer movement */
921 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
922 - _last_pointer_time_axis_view
923 - delta_time_axis_view;
926 /* Verify change in y */
927 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
928 /* this y movement is not allowed, so do no y movement this time */
929 delta_time_axis_view = 0;
934 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
935 /* haven't reached next snap point, and we're not switching
936 trackviews nor layers. nothing to do.
941 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
942 PlaylistDropzoneMap playlist_dropzone_map;
943 _ndropzone = 0; // number of elements currently in the dropzone
946 /* sort views by time_axis.
947 * This retains track order in the dropzone, regardless
948 * of actual selection order
950 _views.sort (DraggingViewSorter());
952 /* count number of distinct tracks of all regions
953 * being dragged, used for dropzone.
956 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
957 if (i->time_axis_view != prev_track) {
958 prev_track = i->time_axis_view;
964 _views.back().time_axis_view -
965 _views.front().time_axis_view;
967 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
968 - _views.back().time_axis_view;
970 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
974 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
976 RegionView* rv = i->view;
981 if (rv->region()->locked() || rv->region()->video_locked()) {
988 /* reparent the regionview into a group above all
992 ArdourCanvas::Item* rvg = rv->get_canvas_group();
993 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
994 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
995 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
996 /* move the item so that it continues to appear at the
997 same location now that its parent has changed.
999 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1002 /* If we have moved tracks, we'll fudge the layer delta so that the
1003 region gets moved back onto layer 0 on its new track; this avoids
1004 confusion when dragging regions from non-zero layers onto different
1007 double this_delta_layer = delta_layer;
1008 if (delta_time_axis_view != 0) {
1009 this_delta_layer = - i->layer;
1012 int this_delta_time_axis_view = delta_time_axis_view;
1013 this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1015 int track_index = i->time_axis_view + this_delta_time_axis_view;
1016 assert(track_index >= 0);
1018 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1019 /* Track is in the Dropzone */
1021 i->time_axis_view = track_index;
1022 assert(i->time_axis_view >= (int) _time_axis_views.size());
1025 double yposition = 0;
1026 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1027 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1030 /* store index of each new playlist as a negative count, starting at -1 */
1032 if (pdz == playlist_dropzone_map.end()) {
1033 /* compute where this new track (which doesn't exist yet) will live
1036 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1038 /* How high is this region view ? */
1040 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1041 ArdourCanvas::Rect bbox;
1044 bbox = obbox.get ();
1047 last_track_bottom_edge += bbox.height();
1049 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1052 yposition = pdz->second;
1055 /* values are zero or negative, hence the use of min() */
1056 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1061 /* The TimeAxisView that this region is now over */
1062 TimeAxisView* current_tv = _time_axis_views[track_index];
1064 /* Ensure it is moved from stacked -> expanded if appropriate */
1065 if (current_tv->view()->layer_display() == Stacked) {
1066 current_tv->view()->set_layer_display (Expanded);
1069 /* We're only allowed to go -ve in layer on Expanded views */
1070 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1071 this_delta_layer = - i->layer;
1075 rv->set_height (current_tv->view()->child_height ());
1077 /* Update show/hidden status as the region view may have come from a hidden track,
1078 or have moved to one.
1080 if (current_tv->hidden ()) {
1081 rv->get_canvas_group()->hide ();
1083 rv->get_canvas_group()->show ();
1086 /* Update the DraggingView */
1087 i->time_axis_view = track_index;
1088 i->layer += this_delta_layer;
1091 _editor->mouse_brush_insert_region (rv, pending_region_position);
1095 /* Get the y coordinate of the top of the track that this region is now over */
1096 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1098 /* And adjust for the layer that it should be on */
1099 StreamView* cv = current_tv->view ();
1100 switch (cv->layer_display ()) {
1104 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1107 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1111 /* need to get the parent of the regionview
1112 * canvas group and get its position in
1113 * equivalent coordinate space as the trackview
1114 * we are now dragging over.
1117 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1122 /* Now move the region view */
1123 rv->move (x_delta, y_delta);
1125 } /* foreach region */
1127 _total_x_delta += x_delta;
1129 if (x_delta != 0 && !_brushing) {
1130 show_verbose_cursor_time (_last_frame_position);
1133 /* keep track of pointer movement */
1135 /* the pointer is currently over a time axis view */
1137 if (_last_pointer_time_axis_view < 0) {
1138 /* last motion event was not over a time axis view
1139 * or last y-movement out of the dropzone was not valid
1142 if (delta_time_axis_view < 0) {
1143 /* in the drop zone, moving up */
1145 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1146 * We do not use negative _last_pointer_time_axis_view because
1147 * the dropzone is "packed" (the actual track offset is ignored)
1149 * As opposed to the actual number
1150 * of elements in the dropzone (_ndropzone)
1151 * _pdropzone is not constrained. This is necessary
1152 * to allow moving multiple regions with y-distance
1155 * There can be 0 elements in the dropzone,
1156 * even though the drag-pointer is inside the DZ.
1159 * [ Audio-track, Midi-track, Audio-track, DZ ]
1160 * move regions from both audio tracks at the same time into the
1161 * DZ by grabbing the region in the bottom track.
1163 assert(current_pointer_time_axis_view >= 0);
1164 dtz = std::min((int)_pdropzone, -delta_time_axis_view);
1168 /* only move out of the zone if the movement is OK */
1169 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1170 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1173 /* last motion event was also over a time axis view */
1174 _last_pointer_time_axis_view += delta_time_axis_view;
1175 assert(_last_pointer_time_axis_view >= 0);
1180 /* the pointer is not over a time axis view */
1181 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1182 _pdropzone += delta_time_axis_view - delta_skip;
1183 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1186 _last_pointer_layer += delta_layer;
1190 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1192 if (_copy && first_move) {
1194 if (_x_constrained) {
1195 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1197 _editor->begin_reversible_command (Operations::region_copy);
1200 /* duplicate the regionview(s) and region(s) */
1202 list<DraggingView> new_regionviews;
1204 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1206 RegionView* rv = i->view;
1207 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1208 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1210 const boost::shared_ptr<const Region> original = rv->region();
1211 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1212 region_copy->set_position (original->position());
1213 /* need to set this so that the drop zone code can work. This doesn't
1214 actually put the region into the playlist, but just sets a weak pointer
1217 region_copy->set_playlist (original->playlist());
1221 boost::shared_ptr<AudioRegion> audioregion_copy
1222 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1224 nrv = new AudioRegionView (*arv, audioregion_copy);
1226 boost::shared_ptr<MidiRegion> midiregion_copy
1227 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1228 nrv = new MidiRegionView (*mrv, midiregion_copy);
1233 nrv->get_canvas_group()->show ();
1234 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1236 /* swap _primary to the copy */
1238 if (rv == _primary) {
1242 /* ..and deselect the one we copied */
1244 rv->set_selected (false);
1247 if (!new_regionviews.empty()) {
1249 /* reflect the fact that we are dragging the copies */
1251 _views = new_regionviews;
1253 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1256 } else if (!_copy && first_move) {
1258 if (_x_constrained) {
1259 _editor->begin_reversible_command (_("fixed time region drag"));
1261 _editor->begin_reversible_command (Operations::region_drag);
1265 RegionMotionDrag::motion (event, first_move);
1269 RegionMotionDrag::finished (GdkEvent *, bool)
1271 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1272 if (!(*i)->view()) {
1276 if ((*i)->view()->layer_display() == Expanded) {
1277 (*i)->view()->set_layer_display (Stacked);
1283 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1285 RegionMotionDrag::finished (ev, movement_occurred);
1287 if (!movement_occurred) {
1291 if (was_double_click() && !_views.empty()) {
1292 DraggingView dv = _views.front();
1293 dv.view->show_region_editor ();
1300 /* reverse this here so that we have the correct logic to finalize
1304 if (Config->get_edit_mode() == Lock) {
1305 _x_constrained = !_x_constrained;
1308 assert (!_views.empty ());
1310 /* We might have hidden region views so that they weren't visible during the drag
1311 (when they have been reparented). Now everything can be shown again, as region
1312 views are back in their track parent groups.
1314 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1315 i->view->get_canvas_group()->show ();
1318 bool const changed_position = (_last_frame_position != _primary->region()->position());
1319 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1320 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1340 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1344 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1346 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1351 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1352 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1353 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1354 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1356 rtav->set_height (original->current_height());
1360 ChanCount one_midi_port (DataType::MIDI, 1);
1361 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1362 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1363 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1365 rtav->set_height (original->current_height());
1370 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1376 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1378 RegionSelection new_views;
1379 PlaylistSet modified_playlists;
1380 RouteTimeAxisView* new_time_axis_view = 0;
1383 /* all changes were made during motion event handlers */
1385 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1389 _editor->commit_reversible_command ();
1393 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1394 PlaylistMapping playlist_mapping;
1396 /* insert the regions into their new playlists */
1397 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1399 RouteTimeAxisView* dest_rtv = 0;
1401 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1407 if (changed_position && !_x_constrained) {
1408 where = i->view->region()->position() - drag_delta;
1410 where = i->view->region()->position();
1413 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1414 /* dragged to drop zone */
1416 PlaylistMapping::iterator pm;
1418 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1419 /* first region from this original playlist: create a new track */
1420 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1421 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1422 dest_rtv = new_time_axis_view;
1424 /* we already created a new track for regions from this playlist, use it */
1425 dest_rtv = pm->second;
1428 /* destination time axis view is the one we dragged to */
1429 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1432 if (dest_rtv != 0) {
1433 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1434 if (new_view != 0) {
1435 new_views.push_back (new_view);
1439 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1440 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1443 list<DraggingView>::const_iterator next = i;
1449 /* If we've created new regions either by copying or moving
1450 to a new track, we want to replace the old selection with the new ones
1453 if (new_views.size() > 0) {
1454 _editor->selection->set (new_views);
1457 /* write commands for the accumulated diffs for all our modified playlists */
1458 add_stateful_diff_commands_for_playlists (modified_playlists);
1460 _editor->commit_reversible_command ();
1464 RegionMoveDrag::finished_no_copy (
1465 bool const changed_position,
1466 bool const changed_tracks,
1467 framecnt_t const drag_delta
1470 RegionSelection new_views;
1471 PlaylistSet modified_playlists;
1472 PlaylistSet frozen_playlists;
1473 set<RouteTimeAxisView*> views_to_update;
1474 RouteTimeAxisView* new_time_axis_view = 0;
1477 /* all changes were made during motion event handlers */
1478 _editor->commit_reversible_command ();
1482 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1483 PlaylistMapping playlist_mapping;
1485 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1487 RegionView* rv = i->view;
1488 RouteTimeAxisView* dest_rtv = 0;
1490 if (rv->region()->locked() || rv->region()->video_locked()) {
1495 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1496 /* dragged to drop zone */
1498 PlaylistMapping::iterator pm;
1500 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1501 /* first region from this original playlist: create a new track */
1502 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1503 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1504 dest_rtv = new_time_axis_view;
1506 /* we already created a new track for regions from this playlist, use it */
1507 dest_rtv = pm->second;
1511 /* destination time axis view is the one we dragged to */
1512 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1517 double const dest_layer = i->layer;
1519 views_to_update.insert (dest_rtv);
1523 if (changed_position && !_x_constrained) {
1524 where = rv->region()->position() - drag_delta;
1526 where = rv->region()->position();
1529 if (changed_tracks) {
1531 /* insert into new playlist */
1533 RegionView* new_view = insert_region_into_playlist (
1534 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1537 if (new_view == 0) {
1542 new_views.push_back (new_view);
1544 /* remove from old playlist */
1546 /* the region that used to be in the old playlist is not
1547 moved to the new one - we use a copy of it. as a result,
1548 any existing editor for the region should no longer be
1551 rv->hide_region_editor();
1554 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1558 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1560 /* this movement may result in a crossfade being modified, or a layering change,
1561 so we need to get undo data from the playlist as well as the region.
1564 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1566 playlist->clear_changes ();
1569 rv->region()->clear_changes ();
1572 motion on the same track. plonk the previously reparented region
1573 back to its original canvas group (its streamview).
1574 No need to do anything for copies as they are fake regions which will be deleted.
1577 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1578 rv->get_canvas_group()->set_y_position (i->initial_y);
1581 /* just change the model */
1582 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1583 playlist->set_layer (rv->region(), dest_layer);
1586 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1588 r = frozen_playlists.insert (playlist);
1591 playlist->freeze ();
1594 rv->region()->set_position (where);
1596 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1599 if (changed_tracks) {
1601 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1602 was selected in all of them, then removing it from a playlist will have removed all
1603 trace of it from _views (i.e. there were N regions selected, we removed 1,
1604 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1605 corresponding regionview, and _views is now empty).
1607 This could have invalidated any and all iterators into _views.
1609 The heuristic we use here is: if the region selection is empty, break out of the loop
1610 here. if the region selection is not empty, then restart the loop because we know that
1611 we must have removed at least the region(view) we've just been working on as well as any
1612 that we processed on previous iterations.
1614 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1615 we can just iterate.
1619 if (_views.empty()) {
1630 /* If we've created new regions either by copying or moving
1631 to a new track, we want to replace the old selection with the new ones
1634 if (new_views.size() > 0) {
1635 _editor->selection->set (new_views);
1638 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1642 /* write commands for the accumulated diffs for all our modified playlists */
1643 add_stateful_diff_commands_for_playlists (modified_playlists);
1645 _editor->commit_reversible_command ();
1647 /* We have futzed with the layering of canvas items on our streamviews.
1648 If any region changed layer, this will have resulted in the stream
1649 views being asked to set up their region views, and all will be well.
1650 If not, we might now have badly-ordered region views. Ask the StreamViews
1651 involved to sort themselves out, just in case.
1654 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1655 (*i)->view()->playlist_layered ((*i)->track ());
1659 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1660 * @param region Region to remove.
1661 * @param playlist playlist To remove from.
1662 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1663 * that clear_changes () is only called once per playlist.
1666 RegionMoveDrag::remove_region_from_playlist (
1667 boost::shared_ptr<Region> region,
1668 boost::shared_ptr<Playlist> playlist,
1669 PlaylistSet& modified_playlists
1672 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1675 playlist->clear_changes ();
1678 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1682 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1683 * clearing the playlist's diff history first if necessary.
1684 * @param region Region to insert.
1685 * @param dest_rtv Destination RouteTimeAxisView.
1686 * @param dest_layer Destination layer.
1687 * @param where Destination position.
1688 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1689 * that clear_changes () is only called once per playlist.
1690 * @return New RegionView, or 0 if no insert was performed.
1693 RegionMoveDrag::insert_region_into_playlist (
1694 boost::shared_ptr<Region> region,
1695 RouteTimeAxisView* dest_rtv,
1698 PlaylistSet& modified_playlists
1701 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1702 if (!dest_playlist) {
1706 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1707 _new_region_view = 0;
1708 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1710 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1711 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1713 dest_playlist->clear_changes ();
1716 dest_playlist->add_region (region, where);
1718 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1719 dest_playlist->set_layer (region, dest_layer);
1724 assert (_new_region_view);
1726 return _new_region_view;
1730 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1732 _new_region_view = rv;
1736 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1738 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1739 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1741 _editor->session()->add_command (c);
1750 RegionMoveDrag::aborted (bool movement_occurred)
1754 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1755 list<DraggingView>::const_iterator next = i;
1764 RegionMotionDrag::aborted (movement_occurred);
1769 RegionMotionDrag::aborted (bool)
1771 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1773 StreamView* sview = (*i)->view();
1776 if (sview->layer_display() == Expanded) {
1777 sview->set_layer_display (Stacked);
1782 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1783 RegionView* rv = i->view;
1784 TimeAxisView* tv = &(rv->get_time_axis_view ());
1785 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1787 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1788 rv->get_canvas_group()->set_y_position (0);
1790 rv->move (-_total_x_delta, 0);
1791 rv->set_height (rtv->view()->child_height ());
1795 /** @param b true to brush, otherwise false.
1796 * @param c true to make copies of the regions being moved, otherwise false.
1798 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1799 : RegionMotionDrag (e, i, p, v, b)
1802 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1805 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1806 if (rtv && rtv->is_track()) {
1807 speed = rtv->track()->speed ();
1810 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1814 RegionMoveDrag::setup_pointer_frame_offset ()
1816 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1819 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1820 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1822 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1824 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1825 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1827 _primary = v->view()->create_region_view (r, false, false);
1829 _primary->get_canvas_group()->show ();
1830 _primary->set_position (pos, 0);
1831 _views.push_back (DraggingView (_primary, this, v));
1833 _last_frame_position = pos;
1835 _item = _primary->get_canvas_group ();
1839 RegionInsertDrag::finished (GdkEvent *, bool)
1841 int pos = _views.front().time_axis_view;
1842 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1844 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1846 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1847 _primary->get_canvas_group()->set_y_position (0);
1849 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1851 _editor->begin_reversible_command (Operations::insert_region);
1852 playlist->clear_changes ();
1853 playlist->add_region (_primary->region (), _last_frame_position);
1855 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1856 if (Config->get_edit_mode() == Ripple) {
1857 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1860 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1861 _editor->commit_reversible_command ();
1869 RegionInsertDrag::aborted (bool)
1876 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1877 : RegionMoveDrag (e, i, p, v, false, false)
1879 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1882 struct RegionSelectionByPosition {
1883 bool operator() (RegionView*a, RegionView* b) {
1884 return a->region()->position () < b->region()->position();
1889 RegionSpliceDrag::motion (GdkEvent* event, bool)
1891 /* Which trackview is this ? */
1893 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1894 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1896 /* The region motion is only processed if the pointer is over
1900 if (!tv || !tv->is_track()) {
1901 /* To make sure we hide the verbose canvas cursor when the mouse is
1902 not held over an audio track.
1904 _editor->verbose_cursor()->hide ();
1907 _editor->verbose_cursor()->show ();
1912 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1918 RegionSelection copy;
1919 _editor->selection->regions.by_position(copy);
1921 framepos_t const pf = adjusted_current_frame (event);
1923 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1925 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1931 boost::shared_ptr<Playlist> playlist;
1933 if ((playlist = atv->playlist()) == 0) {
1937 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1942 if (pf < (*i)->region()->last_frame() + 1) {
1946 if (pf > (*i)->region()->first_frame()) {
1952 playlist->shuffle ((*i)->region(), dir);
1957 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1959 RegionMoveDrag::finished (event, movement_occurred);
1963 RegionSpliceDrag::aborted (bool)
1973 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1976 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1978 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1979 RegionSelection to_ripple;
1980 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1981 if ((*i)->position() >= where) {
1982 to_ripple.push_back (rtv->view()->find_view(*i));
1986 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1987 if (!exclude.contains (*i)) {
1988 // the selection has already been added to _views
1990 if (drag_in_progress) {
1991 // do the same things that RegionMotionDrag::motion does when
1992 // first_move is true, for the region views that we're adding
1993 // to _views this time
1996 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1997 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1998 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1999 rvg->reparent (_editor->_drag_motion_group);
2001 // we only need to move in the y direction
2002 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2007 _views.push_back (DraggingView (*i, this, tav));
2013 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2016 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2017 // we added all the regions after the selection
2019 std::list<DraggingView>::iterator to_erase = i++;
2020 if (!_editor->selection->regions.contains (to_erase->view)) {
2021 // restore the non-selected regions to their original playlist & positions,
2022 // and then ripple them back by the length of the regions that were dragged away
2023 // do the same things as RegionMotionDrag::aborted
2025 RegionView *rv = to_erase->view;
2026 TimeAxisView* tv = &(rv->get_time_axis_view ());
2027 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2030 // plonk them back onto their own track
2031 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2032 rv->get_canvas_group()->set_y_position (0);
2036 // move the underlying region to match the view
2037 rv->region()->set_position (rv->region()->position() + amount);
2039 // restore the view to match the underlying region's original position
2040 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2043 rv->set_height (rtv->view()->child_height ());
2044 _views.erase (to_erase);
2050 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2052 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2054 return allow_moves_across_tracks;
2062 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2063 : RegionMoveDrag (e, i, p, v, false, false)
2065 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2066 // compute length of selection
2067 RegionSelection selected_regions = _editor->selection->regions;
2068 selection_length = selected_regions.end_frame() - selected_regions.start();
2070 // we'll only allow dragging to another track in ripple mode if all the regions
2071 // being dragged start off on the same track
2072 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2075 exclude = new RegionList;
2076 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2077 exclude->push_back((*i)->region());
2080 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2081 RegionSelection copy;
2082 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2084 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2085 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2087 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2088 // find ripple start point on each applicable playlist
2089 RegionView *first_selected_on_this_track = NULL;
2090 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2091 if ((*i)->region()->playlist() == (*pi)) {
2092 // region is on this playlist - it's the first, because they're sorted
2093 first_selected_on_this_track = *i;
2097 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2098 add_all_after_to_views (
2099 &first_selected_on_this_track->get_time_axis_view(),
2100 first_selected_on_this_track->region()->position(),
2101 selected_regions, false);
2104 if (allow_moves_across_tracks) {
2105 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2113 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2115 /* Which trackview is this ? */
2117 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2118 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2120 /* The region motion is only processed if the pointer is over
2124 if (!tv || !tv->is_track()) {
2125 /* To make sure we hide the verbose canvas cursor when the mouse is
2126 not held over an audiotrack.
2128 _editor->verbose_cursor()->hide ();
2132 framepos_t where = adjusted_current_frame (event);
2133 assert (where >= 0);
2135 double delta = compute_x_delta (event, &after);
2137 framecnt_t amount = _editor->pixel_to_sample (delta);
2139 if (allow_moves_across_tracks) {
2140 // all the originally selected regions were on the same track
2142 framecnt_t adjust = 0;
2143 if (prev_tav && tv != prev_tav) {
2144 // dragged onto a different track
2145 // remove the unselected regions from _views, restore them to their original positions
2146 // and add the regions after the drop point on the new playlist to _views instead.
2147 // undo the effect of rippling the previous playlist, and include the effect of removing
2148 // the dragged region(s) from this track
2150 remove_unselected_from_views (prev_amount, false);
2151 // ripple previous playlist according to the regions that have been removed onto the new playlist
2152 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2155 // move just the selected regions
2156 RegionMoveDrag::motion(event, first_move);
2158 // ensure that the ripple operation on the new playlist inserts selection_length time
2159 adjust = selection_length;
2160 // ripple the new current playlist
2161 tv->playlist()->ripple (where, amount+adjust, exclude);
2163 // add regions after point where drag entered this track to subsequent ripples
2164 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2167 // motion on same track
2168 RegionMoveDrag::motion(event, first_move);
2172 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2173 prev_position = where;
2175 // selection encompasses multiple tracks - just drag
2176 // cross-track drags are forbidden
2177 RegionMoveDrag::motion(event, first_move);
2180 if (!_x_constrained) {
2181 prev_amount += amount;
2184 _last_frame_position = after;
2188 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2190 if (!movement_occurred) {
2194 if (was_double_click() && !_views.empty()) {
2195 DraggingView dv = _views.front();
2196 dv.view->show_region_editor ();
2203 _editor->begin_reversible_command(_("Ripple drag"));
2205 // remove the regions being rippled from the dragging view, updating them to
2206 // their new positions
2207 remove_unselected_from_views (prev_amount, true);
2209 if (allow_moves_across_tracks) {
2211 // if regions were dragged across tracks, we've rippled any later
2212 // regions on the track the regions were dragged off, so we need
2213 // to add the original track to the undo record
2214 orig_tav->playlist()->clear_changes();
2215 vector<Command*> cmds;
2216 orig_tav->playlist()->rdiff (cmds);
2217 _editor->session()->add_commands (cmds);
2219 if (prev_tav && prev_tav != orig_tav) {
2220 prev_tav->playlist()->clear_changes();
2221 vector<Command*> cmds;
2222 prev_tav->playlist()->rdiff (cmds);
2223 _editor->session()->add_commands (cmds);
2226 // selection spanned multiple tracks - all will need adding to undo record
2228 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2229 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2231 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2232 (*pi)->clear_changes();
2233 vector<Command*> cmds;
2234 (*pi)->rdiff (cmds);
2235 _editor->session()->add_commands (cmds);
2239 // other modified playlists are added to undo by RegionMoveDrag::finished()
2240 RegionMoveDrag::finished (event, movement_occurred);
2241 _editor->commit_reversible_command();
2245 RegionRippleDrag::aborted (bool movement_occurred)
2247 RegionMoveDrag::aborted (movement_occurred);
2252 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2254 _view (dynamic_cast<MidiTimeAxisView*> (v))
2256 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2262 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2265 _region = add_midi_region (_view);
2266 _view->playlist()->freeze ();
2269 framepos_t const f = adjusted_current_frame (event);
2270 if (f < grab_frame()) {
2271 _region->set_position (f);
2274 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2275 so that if this region is duplicated, its duplicate starts on
2276 a snap point rather than 1 frame after a snap point. Otherwise things get
2277 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2278 place snapped notes at the start of the region.
2281 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2282 _region->set_length (len < 1 ? 1 : len);
2288 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2290 if (!movement_occurred) {
2291 add_midi_region (_view);
2293 _view->playlist()->thaw ();
2298 RegionCreateDrag::aborted (bool)
2301 _view->playlist()->thaw ();
2307 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2311 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2315 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2317 Gdk::Cursor* cursor;
2318 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2320 float x_fraction = cnote->mouse_x_fraction ();
2322 if (x_fraction > 0.0 && x_fraction < 0.25) {
2323 cursor = _editor->cursors()->left_side_trim;
2326 cursor = _editor->cursors()->right_side_trim;
2330 Drag::start_grab (event, cursor);
2332 region = &cnote->region_view();
2336 if (event->motion.state & Keyboard::PrimaryModifier) {
2342 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2344 if (ms.size() > 1) {
2345 /* has to be relative, may make no sense otherwise */
2349 /* select this note; if it is already selected, preserve the existing selection,
2350 otherwise make this note the only one selected.
2352 region->note_selected (cnote, cnote->selected ());
2354 _editor->begin_reversible_command (_("resize notes"));
2356 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2357 MidiRegionSelection::iterator next;
2360 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2362 mrv->begin_resizing (at_front);
2369 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
2371 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2372 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2373 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2375 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2377 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2383 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
2385 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2386 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2387 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2389 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2391 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2395 _editor->commit_reversible_command ();
2399 NoteResizeDrag::aborted (bool)
2401 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2402 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2403 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2405 mrv->abort_resizing ();
2410 AVDraggingView::AVDraggingView (RegionView* v)
2413 initial_position = v->region()->position ();
2416 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2419 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2422 TrackViewList empty;
2424 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2425 std::list<RegionView*> views = rs.by_layer();
2427 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2428 RegionView* rv = (*i);
2429 if (!rv->region()->video_locked()) {
2432 _views.push_back (AVDraggingView (rv));
2437 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2439 Drag::start_grab (event);
2440 if (_editor->session() == 0) {
2444 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2445 _max_backwards_drag = (
2446 ARDOUR_UI::instance()->video_timeline->get_duration()
2447 + ARDOUR_UI::instance()->video_timeline->get_offset()
2448 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2451 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2452 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2453 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2456 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2459 Timecode::Time timecode;
2460 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2461 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);
2462 show_verbose_cursor_text (buf);
2466 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2468 if (_editor->session() == 0) {
2471 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2475 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2476 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2478 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2479 dt = - _max_backwards_drag;
2482 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2483 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2485 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2486 RegionView* rv = i->view;
2487 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2490 rv->region()->clear_changes ();
2491 rv->region()->suspend_property_changes();
2493 rv->region()->set_position(i->initial_position + dt);
2494 rv->region_changed(ARDOUR::Properties::position);
2497 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2498 Timecode::Time timecode;
2499 Timecode::Time timediff;
2501 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2502 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2503 snprintf (buf, sizeof (buf),
2504 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2505 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2506 , _("Video Start:"),
2507 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2509 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2511 show_verbose_cursor_text (buf);
2515 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2517 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2521 if (!movement_occurred || ! _editor->session()) {
2525 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2527 _editor->begin_reversible_command (_("Move Video"));
2529 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2530 ARDOUR_UI::instance()->video_timeline->save_undo();
2531 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2532 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2534 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2535 i->view->drag_end();
2536 i->view->region()->resume_property_changes ();
2538 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2541 _editor->session()->maybe_update_session_range(
2542 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2543 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2547 _editor->commit_reversible_command ();
2551 VideoTimeLineDrag::aborted (bool)
2553 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2556 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2557 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2559 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2560 i->view->region()->resume_property_changes ();
2561 i->view->region()->set_position(i->initial_position);
2565 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2566 : RegionDrag (e, i, p, v)
2567 , _preserve_fade_anchor (preserve_fade_anchor)
2568 , _jump_position_when_done (false)
2570 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2574 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2577 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2578 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2580 if (tv && tv->is_track()) {
2581 speed = tv->track()->speed();
2584 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2585 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2586 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2588 framepos_t const pf = adjusted_current_frame (event);
2590 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2591 /* Move the contents of the region around without changing the region bounds */
2592 _operation = ContentsTrim;
2593 Drag::start_grab (event, _editor->cursors()->trimmer);
2595 /* These will get overridden for a point trim.*/
2596 if (pf < (region_start + region_length/2)) {
2597 /* closer to front */
2598 _operation = StartTrim;
2600 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2601 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2603 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2607 _operation = EndTrim;
2608 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2609 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2611 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2616 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2617 _jump_position_when_done = true;
2620 switch (_operation) {
2622 show_verbose_cursor_time (region_start);
2623 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2624 i->view->trim_front_starting ();
2628 show_verbose_cursor_time (region_end);
2631 show_verbose_cursor_time (pf);
2635 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2636 i->view->region()->suspend_property_changes ();
2641 TrimDrag::motion (GdkEvent* event, bool first_move)
2643 RegionView* rv = _primary;
2646 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2647 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2648 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2649 frameoffset_t frame_delta = 0;
2651 if (tv && tv->is_track()) {
2652 speed = tv->track()->speed();
2655 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2661 switch (_operation) {
2663 trim_type = "Region start trim";
2666 trim_type = "Region end trim";
2669 trim_type = "Region content trim";
2676 _editor->begin_reversible_command (trim_type);
2678 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2679 RegionView* rv = i->view;
2680 rv->enable_display (false);
2681 rv->region()->playlist()->clear_owned_changes ();
2683 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2686 arv->temporarily_hide_envelope ();
2690 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2691 insert_result = _editor->motion_frozen_playlists.insert (pl);
2693 if (insert_result.second) {
2699 bool non_overlap_trim = false;
2701 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2702 non_overlap_trim = true;
2705 /* contstrain trim to fade length */
2706 if (_preserve_fade_anchor) {
2707 switch (_operation) {
2709 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2710 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2712 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2713 if (ar->locked()) continue;
2714 framecnt_t len = ar->fade_in()->back()->when;
2715 if (len < dt) dt = min(dt, len);
2719 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2720 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2722 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2723 if (ar->locked()) continue;
2724 framecnt_t len = ar->fade_out()->back()->when;
2725 if (len < -dt) dt = max(dt, -len);
2734 switch (_operation) {
2736 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2737 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2738 if (changed && _preserve_fade_anchor) {
2739 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2741 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2742 framecnt_t len = ar->fade_in()->back()->when;
2743 framecnt_t diff = ar->first_frame() - i->initial_position;
2744 framepos_t new_length = len - diff;
2745 i->anchored_fade_length = min (ar->length(), new_length);
2746 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2747 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2754 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2755 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2756 if (changed && _preserve_fade_anchor) {
2757 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2759 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2760 framecnt_t len = ar->fade_out()->back()->when;
2761 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2762 framepos_t new_length = len + diff;
2763 i->anchored_fade_length = min (ar->length(), new_length);
2764 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2765 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2773 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2775 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2776 i->view->move_contents (frame_delta);
2782 switch (_operation) {
2784 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2787 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2790 // show_verbose_cursor_time (frame_delta);
2797 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2799 if (movement_occurred) {
2800 motion (event, false);
2802 if (_operation == StartTrim) {
2803 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2805 /* This must happen before the region's StatefulDiffCommand is created, as it may
2806 `correct' (ahem) the region's _start from being negative to being zero. It
2807 needs to be zero in the undo record.
2809 i->view->trim_front_ending ();
2811 if (_preserve_fade_anchor) {
2812 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2814 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2815 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2816 ar->set_fade_in_length(i->anchored_fade_length);
2817 ar->set_fade_in_active(true);
2820 if (_jump_position_when_done) {
2821 i->view->region()->set_position (i->initial_position);
2824 } else if (_operation == EndTrim) {
2825 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2826 if (_preserve_fade_anchor) {
2827 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2829 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2830 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2831 ar->set_fade_out_length(i->anchored_fade_length);
2832 ar->set_fade_out_active(true);
2835 if (_jump_position_when_done) {
2836 i->view->region()->set_position (i->initial_end - i->view->region()->length());
2841 if (!_views.empty()) {
2842 if (_operation == StartTrim) {
2843 _editor->maybe_locate_with_edit_preroll(
2844 _views.begin()->view->region()->position());
2846 if (_operation == EndTrim) {
2847 _editor->maybe_locate_with_edit_preroll(
2848 _views.begin()->view->region()->position() +
2849 _views.begin()->view->region()->length());
2853 if (!_editor->selection->selected (_primary)) {
2854 _primary->thaw_after_trim ();
2857 set<boost::shared_ptr<Playlist> > diffed_playlists;
2859 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2860 i->view->thaw_after_trim ();
2861 i->view->enable_display (true);
2863 /* Trimming one region may affect others on the playlist, so we need
2864 to get undo Commands from the whole playlist rather than just the
2865 region. Use diffed_playlists to make sure we don't diff a given
2866 playlist more than once.
2868 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2869 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2870 vector<Command*> cmds;
2872 _editor->session()->add_commands (cmds);
2873 diffed_playlists.insert (p);
2878 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2882 _editor->motion_frozen_playlists.clear ();
2883 _editor->commit_reversible_command();
2886 /* no mouse movement */
2887 _editor->point_trim (event, adjusted_current_frame (event));
2890 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2891 if (_operation == StartTrim) {
2892 i->view->trim_front_ending ();
2895 i->view->region()->resume_property_changes ();
2900 TrimDrag::aborted (bool movement_occurred)
2902 /* Our motion method is changing model state, so use the Undo system
2903 to cancel. Perhaps not ideal, as this will leave an Undo point
2904 behind which may be slightly odd from the user's point of view.
2909 if (movement_occurred) {
2913 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2914 i->view->region()->resume_property_changes ();
2919 TrimDrag::setup_pointer_frame_offset ()
2921 list<DraggingView>::iterator i = _views.begin ();
2922 while (i != _views.end() && i->view != _primary) {
2926 if (i == _views.end()) {
2930 switch (_operation) {
2932 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2935 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2942 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2946 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2947 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2952 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2954 Drag::start_grab (event, cursor);
2955 show_verbose_cursor_time (adjusted_current_frame(event));
2959 MeterMarkerDrag::setup_pointer_frame_offset ()
2961 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2965 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2967 if (!_marker->meter().movable()) {
2973 // create a dummy marker for visual representation of moving the
2974 // section, because whether its a copy or not, we're going to
2975 // leave or lose the original marker (leave if its a copy; lose if its
2976 // not, because we'll remove it from the map).
2978 MeterSection section (_marker->meter());
2980 if (!section.movable()) {
2985 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2987 _marker = new MeterMarker (
2989 *_editor->meter_group,
2990 ARDOUR_UI::config()->color ("meter marker"),
2992 *new MeterSection (_marker->meter())
2995 /* use the new marker for the grab */
2996 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2999 TempoMap& map (_editor->session()->tempo_map());
3000 /* get current state */
3001 before_state = &map.get_state();
3002 /* remove the section while we drag it */
3003 map.remove_meter (section, true);
3007 framepos_t const pf = adjusted_current_frame (event);
3009 _marker->set_position (pf);
3010 show_verbose_cursor_time (pf);
3014 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3016 if (!movement_occurred) {
3017 if (was_double_click()) {
3018 _editor->edit_meter_marker (*_marker);
3023 if (!_marker->meter().movable()) {
3027 motion (event, false);
3029 Timecode::BBT_Time when;
3031 TempoMap& map (_editor->session()->tempo_map());
3032 map.bbt_time (last_pointer_frame(), when);
3034 if (_copy == true) {
3035 _editor->begin_reversible_command (_("copy meter mark"));
3036 XMLNode &before = map.get_state();
3037 map.add_meter (_marker->meter(), when);
3038 XMLNode &after = map.get_state();
3039 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3040 _editor->commit_reversible_command ();
3043 _editor->begin_reversible_command (_("move meter mark"));
3045 /* we removed it before, so add it back now */
3047 map.add_meter (_marker->meter(), when);
3048 XMLNode &after = map.get_state();
3049 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3050 _editor->commit_reversible_command ();
3053 // delete the dummy marker we used for visual representation while moving.
3054 // a new visual marker will show up automatically.
3059 MeterMarkerDrag::aborted (bool moved)
3061 _marker->set_position (_marker->meter().frame ());
3064 TempoMap& map (_editor->session()->tempo_map());
3065 /* we removed it before, so add it back now */
3066 map.add_meter (_marker->meter(), _marker->meter().frame());
3067 // delete the dummy marker we used for visual representation while moving.
3068 // a new visual marker will show up automatically.
3073 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3077 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3079 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3084 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3086 Drag::start_grab (event, cursor);
3087 show_verbose_cursor_time (adjusted_current_frame (event));
3091 TempoMarkerDrag::setup_pointer_frame_offset ()
3093 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3097 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3099 if (!_marker->tempo().movable()) {
3105 // create a dummy marker for visual representation of moving the
3106 // section, because whether its a copy or not, we're going to
3107 // leave or lose the original marker (leave if its a copy; lose if its
3108 // not, because we'll remove it from the map).
3110 // create a dummy marker for visual representation of moving the copy.
3111 // The actual copying is not done before we reach the finish callback.
3114 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3116 TempoSection section (_marker->tempo());
3118 _marker = new TempoMarker (
3120 *_editor->tempo_group,
3121 ARDOUR_UI::config()->color ("tempo marker"),
3123 *new TempoSection (_marker->tempo())
3126 /* use the new marker for the grab */
3127 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3130 TempoMap& map (_editor->session()->tempo_map());
3131 /* get current state */
3132 before_state = &map.get_state();
3133 /* remove the section while we drag it */
3134 map.remove_tempo (section, true);
3138 framepos_t const pf = adjusted_current_frame (event);
3139 _marker->set_position (pf);
3140 show_verbose_cursor_time (pf);
3144 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3146 if (!movement_occurred) {
3147 if (was_double_click()) {
3148 _editor->edit_tempo_marker (*_marker);
3153 if (!_marker->tempo().movable()) {
3157 motion (event, false);
3159 TempoMap& map (_editor->session()->tempo_map());
3160 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3161 Timecode::BBT_Time when;
3163 map.bbt_time (beat_time, when);
3165 if (_copy == true) {
3166 _editor->begin_reversible_command (_("copy tempo mark"));
3167 XMLNode &before = map.get_state();
3168 map.add_tempo (_marker->tempo(), when);
3169 XMLNode &after = map.get_state();
3170 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3171 _editor->commit_reversible_command ();
3174 _editor->begin_reversible_command (_("move tempo mark"));
3175 /* we removed it before, so add it back now */
3176 map.add_tempo (_marker->tempo(), when);
3177 XMLNode &after = map.get_state();
3178 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3179 _editor->commit_reversible_command ();
3182 // delete the dummy marker we used for visual representation while moving.
3183 // a new visual marker will show up automatically.
3188 TempoMarkerDrag::aborted (bool moved)
3190 _marker->set_position (_marker->tempo().frame());
3192 TempoMap& map (_editor->session()->tempo_map());
3193 /* we removed it before, so add it back now */
3194 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3195 // delete the dummy marker we used for visual representation while moving.
3196 // a new visual marker will show up automatically.
3201 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3202 : Drag (e, &c.track_canvas_item(), false)
3206 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3209 /** Do all the things we do when dragging the playhead to make it look as though
3210 * we have located, without actually doing the locate (because that would cause
3211 * the diskstream buffers to be refilled, which is too slow).
3214 CursorDrag::fake_locate (framepos_t t)
3216 _editor->playhead_cursor->set_position (t);
3218 Session* s = _editor->session ();
3219 if (s->timecode_transmission_suspended ()) {
3220 framepos_t const f = _editor->playhead_cursor->current_frame ();
3221 /* This is asynchronous so it will be sent "now"
3223 s->send_mmc_locate (f);
3224 /* These are synchronous and will be sent during the next
3227 s->queue_full_time_code ();
3228 s->queue_song_position_pointer ();
3231 show_verbose_cursor_time (t);
3232 _editor->UpdateAllTransportClocks (t);
3236 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3238 Drag::start_grab (event, c);
3240 _grab_zoom = _editor->samples_per_pixel;
3242 framepos_t where = _editor->canvas_event_sample (event);
3244 _editor->snap_to_with_modifier (where, event);
3246 _editor->_dragging_playhead = true;
3248 Session* s = _editor->session ();
3250 /* grab the track canvas item as well */
3252 _cursor.track_canvas_item().grab();
3255 if (_was_rolling && _stop) {
3259 if (s->is_auditioning()) {
3260 s->cancel_audition ();
3264 if (AudioEngine::instance()->connected()) {
3266 /* do this only if we're the engine is connected
3267 * because otherwise this request will never be
3268 * serviced and we'll busy wait forever. likewise,
3269 * notice if we are disconnected while waiting for the
3270 * request to be serviced.
3273 s->request_suspend_timecode_transmission ();
3274 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3275 /* twiddle our thumbs */
3280 fake_locate (where);
3284 CursorDrag::motion (GdkEvent* event, bool)
3286 framepos_t const adjusted_frame = adjusted_current_frame (event);
3287 if (adjusted_frame != last_pointer_frame()) {
3288 fake_locate (adjusted_frame);
3293 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3295 _editor->_dragging_playhead = false;
3297 _cursor.track_canvas_item().ungrab();
3299 if (!movement_occurred && _stop) {
3303 motion (event, false);
3305 Session* s = _editor->session ();
3307 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3308 _editor->_pending_locate_request = true;
3309 s->request_resume_timecode_transmission ();
3314 CursorDrag::aborted (bool)
3316 _cursor.track_canvas_item().ungrab();
3318 if (_editor->_dragging_playhead) {
3319 _editor->session()->request_resume_timecode_transmission ();
3320 _editor->_dragging_playhead = false;
3323 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3326 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3327 : RegionDrag (e, i, p, v)
3329 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3333 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3335 Drag::start_grab (event, cursor);
3337 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3338 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3340 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3344 FadeInDrag::setup_pointer_frame_offset ()
3346 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3347 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3348 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3352 FadeInDrag::motion (GdkEvent* event, bool)
3354 framecnt_t fade_length;
3355 framepos_t const pos = adjusted_current_frame (event);
3356 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3358 if (pos < (region->position() + 64)) {
3359 fade_length = 64; // this should be a minimum defined somewhere
3360 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3361 fade_length = region->length() - region->fade_out()->back()->when - 1;
3363 fade_length = pos - region->position();
3366 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3368 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3374 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3377 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3381 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3383 if (!movement_occurred) {
3387 framecnt_t fade_length;
3389 framepos_t const pos = adjusted_current_frame (event);
3391 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3393 if (pos < (region->position() + 64)) {
3394 fade_length = 64; // this should be a minimum defined somewhere
3395 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3396 fade_length = region->length() - region->fade_out()->back()->when - 1;
3398 fade_length = pos - region->position();
3401 _editor->begin_reversible_command (_("change fade in length"));
3403 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3405 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3411 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3412 XMLNode &before = alist->get_state();
3414 tmp->audio_region()->set_fade_in_length (fade_length);
3415 tmp->audio_region()->set_fade_in_active (true);
3417 XMLNode &after = alist->get_state();
3418 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3421 _editor->commit_reversible_command ();
3425 FadeInDrag::aborted (bool)
3427 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3428 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3434 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3438 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3439 : RegionDrag (e, i, p, v)
3441 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3445 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3447 Drag::start_grab (event, cursor);
3449 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3450 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3452 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3456 FadeOutDrag::setup_pointer_frame_offset ()
3458 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3459 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3460 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3464 FadeOutDrag::motion (GdkEvent* event, bool)
3466 framecnt_t fade_length;
3468 framepos_t const pos = adjusted_current_frame (event);
3470 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3472 if (pos > (region->last_frame() - 64)) {
3473 fade_length = 64; // this should really be a minimum fade defined somewhere
3474 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3475 fade_length = region->length() - region->fade_in()->back()->when - 1;
3477 fade_length = region->last_frame() - pos;
3480 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3482 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3488 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3491 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3495 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3497 if (!movement_occurred) {
3501 framecnt_t fade_length;
3503 framepos_t const pos = adjusted_current_frame (event);
3505 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3507 if (pos > (region->last_frame() - 64)) {
3508 fade_length = 64; // this should really be a minimum fade defined somewhere
3509 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3510 fade_length = region->length() - region->fade_in()->back()->when - 1;
3512 fade_length = region->last_frame() - pos;
3515 _editor->begin_reversible_command (_("change fade out length"));
3517 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3519 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3525 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3526 XMLNode &before = alist->get_state();
3528 tmp->audio_region()->set_fade_out_length (fade_length);
3529 tmp->audio_region()->set_fade_out_active (true);
3531 XMLNode &after = alist->get_state();
3532 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3535 _editor->commit_reversible_command ();
3539 FadeOutDrag::aborted (bool)
3541 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3542 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3548 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3552 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3555 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3557 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3560 _points.push_back (ArdourCanvas::Duple (0, 0));
3561 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3564 MarkerDrag::~MarkerDrag ()
3566 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3571 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3573 location = new Location (*l);
3574 markers.push_back (m);
3579 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3581 Drag::start_grab (event, cursor);
3585 Location *location = _editor->find_location_from_marker (_marker, is_start);
3586 _editor->_dragging_edit_point = true;
3588 update_item (location);
3590 // _drag_line->show();
3591 // _line->raise_to_top();
3594 show_verbose_cursor_time (location->start());
3596 show_verbose_cursor_time (location->end());
3599 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3602 case Selection::Toggle:
3603 /* we toggle on the button release */
3605 case Selection::Set:
3606 if (!_editor->selection->selected (_marker)) {
3607 _editor->selection->set (_marker);
3610 case Selection::Extend:
3612 Locations::LocationList ll;
3613 list<Marker*> to_add;
3615 _editor->selection->markers.range (s, e);
3616 s = min (_marker->position(), s);
3617 e = max (_marker->position(), e);
3620 if (e < max_framepos) {
3623 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3624 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3625 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3628 to_add.push_back (lm->start);
3631 to_add.push_back (lm->end);
3635 if (!to_add.empty()) {
3636 _editor->selection->add (to_add);
3640 case Selection::Add:
3641 _editor->selection->add (_marker);
3645 /* Set up copies for us to manipulate during the drag
3648 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3650 Location* l = _editor->find_location_from_marker (*i, is_start);
3657 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3659 /* range: check that the other end of the range isn't
3662 CopiedLocationInfo::iterator x;
3663 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3664 if (*(*x).location == *l) {
3668 if (x == _copied_locations.end()) {
3669 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3671 (*x).markers.push_back (*i);
3672 (*x).move_both = true;
3680 MarkerDrag::setup_pointer_frame_offset ()
3683 Location *location = _editor->find_location_from_marker (_marker, is_start);
3684 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3688 MarkerDrag::motion (GdkEvent* event, bool)
3690 framecnt_t f_delta = 0;
3692 bool move_both = false;
3693 Location *real_location;
3694 Location *copy_location = 0;
3696 framepos_t const newframe = adjusted_current_frame (event);
3697 framepos_t next = newframe;
3699 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3703 CopiedLocationInfo::iterator x;
3705 /* find the marker we're dragging, and compute the delta */
3707 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3709 copy_location = (*x).location;
3711 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3713 /* this marker is represented by this
3714 * CopiedLocationMarkerInfo
3717 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3722 if (real_location->is_mark()) {
3723 f_delta = newframe - copy_location->start();
3727 switch (_marker->type()) {
3728 case Marker::SessionStart:
3729 case Marker::RangeStart:
3730 case Marker::LoopStart:
3731 case Marker::PunchIn:
3732 f_delta = newframe - copy_location->start();
3735 case Marker::SessionEnd:
3736 case Marker::RangeEnd:
3737 case Marker::LoopEnd:
3738 case Marker::PunchOut:
3739 f_delta = newframe - copy_location->end();
3742 /* what kind of marker is this ? */
3751 if (x == _copied_locations.end()) {
3752 /* hmm, impossible - we didn't find the dragged marker */
3756 /* now move them all */
3758 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3760 copy_location = x->location;
3762 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3766 if (real_location->locked()) {
3770 if (copy_location->is_mark()) {
3774 copy_location->set_start (copy_location->start() + f_delta);
3778 framepos_t new_start = copy_location->start() + f_delta;
3779 framepos_t new_end = copy_location->end() + f_delta;
3781 if (is_start) { // start-of-range marker
3783 if (move_both || (*x).move_both) {
3784 copy_location->set_start (new_start);
3785 copy_location->set_end (new_end);
3786 } else if (new_start < copy_location->end()) {
3787 copy_location->set_start (new_start);
3788 } else if (newframe > 0) {
3789 _editor->snap_to (next, RoundUpAlways, true);
3790 copy_location->set_end (next);
3791 copy_location->set_start (newframe);
3794 } else { // end marker
3796 if (move_both || (*x).move_both) {
3797 copy_location->set_end (new_end);
3798 copy_location->set_start (new_start);
3799 } else if (new_end > copy_location->start()) {
3800 copy_location->set_end (new_end);
3801 } else if (newframe > 0) {
3802 _editor->snap_to (next, RoundDownAlways, true);
3803 copy_location->set_start (next);
3804 copy_location->set_end (newframe);
3809 update_item (copy_location);
3811 /* now lookup the actual GUI items used to display this
3812 * location and move them to wherever the copy of the location
3813 * is now. This means that the logic in ARDOUR::Location is
3814 * still enforced, even though we are not (yet) modifying
3815 * the real Location itself.
3818 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3821 lm->set_position (copy_location->start(), copy_location->end());
3826 assert (!_copied_locations.empty());
3828 show_verbose_cursor_time (newframe);
3832 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3834 if (!movement_occurred) {
3836 if (was_double_click()) {
3837 _editor->rename_marker (_marker);
3841 /* just a click, do nothing but finish
3842 off the selection process
3845 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3848 case Selection::Set:
3849 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3850 _editor->selection->set (_marker);
3854 case Selection::Toggle:
3855 /* we toggle on the button release, click only */
3856 _editor->selection->toggle (_marker);
3859 case Selection::Extend:
3860 case Selection::Add:
3867 _editor->_dragging_edit_point = false;
3869 _editor->begin_reversible_command ( _("move marker") );
3870 XMLNode &before = _editor->session()->locations()->get_state();
3872 MarkerSelection::iterator i;
3873 CopiedLocationInfo::iterator x;
3876 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3877 x != _copied_locations.end() && i != _editor->selection->markers.end();
3880 Location * location = _editor->find_location_from_marker (*i, is_start);
3884 if (location->locked()) {
3888 if (location->is_mark()) {
3889 location->set_start (((*x).location)->start());
3891 location->set (((*x).location)->start(), ((*x).location)->end());
3896 XMLNode &after = _editor->session()->locations()->get_state();
3897 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3898 _editor->commit_reversible_command ();
3902 MarkerDrag::aborted (bool movement_occured)
3904 if (!movement_occured) {
3908 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3910 /* move all markers to their original location */
3913 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3916 Location * location = _editor->find_location_from_marker (*m, is_start);
3919 (*m)->set_position (is_start ? location->start() : location->end());
3926 MarkerDrag::update_item (Location*)
3931 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3933 _cumulative_x_drag (0),
3934 _cumulative_y_drag (0)
3936 if (_zero_gain_fraction < 0.0) {
3937 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3940 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3942 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3948 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3950 Drag::start_grab (event, _editor->cursors()->fader);
3952 // start the grab at the center of the control point so
3953 // the point doesn't 'jump' to the mouse after the first drag
3954 _fixed_grab_x = _point->get_x();
3955 _fixed_grab_y = _point->get_y();
3957 float const fraction = 1 - (_point->get_y() / _point->line().height());
3959 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3961 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3963 _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3965 if (!_point->can_slide ()) {
3966 _x_constrained = true;
3971 ControlPointDrag::motion (GdkEvent* event, bool)
3973 double dx = _drags->current_pointer_x() - last_pointer_x();
3974 double dy = current_pointer_y() - last_pointer_y();
3976 if (event->button.state & Keyboard::SecondaryModifier) {
3981 /* coordinate in pixels relative to the start of the region (for region-based automation)
3982 or track (for track-based automation) */
3983 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3984 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3986 // calculate zero crossing point. back off by .01 to stay on the
3987 // positive side of zero
3988 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3990 // make sure we hit zero when passing through
3991 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3995 if (_x_constrained) {
3998 if (_y_constrained) {
4002 _cumulative_x_drag = cx - _fixed_grab_x;
4003 _cumulative_y_drag = cy - _fixed_grab_y;
4007 cy = min ((double) _point->line().height(), cy);
4009 framepos_t cx_frames = _editor->pixel_to_sample (cx);
4011 if (!_x_constrained) {
4012 _editor->snap_to_with_modifier (cx_frames, event);
4015 cx_frames = min (cx_frames, _point->line().maximum_time());
4017 float const fraction = 1.0 - (cy / _point->line().height());
4019 _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4021 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4025 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4027 if (!movement_occurred) {
4031 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4032 _editor->reset_point_selection ();
4036 motion (event, false);
4039 _point->line().end_drag (_pushing, _final_index);
4040 _editor->commit_reversible_command ();
4044 ControlPointDrag::aborted (bool)
4046 _point->line().reset ();
4050 ControlPointDrag::active (Editing::MouseMode m)
4052 if (m == Editing::MouseDraw) {
4053 /* always active in mouse draw */
4057 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4058 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4061 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4064 _cumulative_y_drag (0)
4066 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4070 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4072 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4075 _item = &_line->grab_item ();
4077 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4078 origin, and ditto for y.
4081 double cx = event->button.x;
4082 double cy = event->button.y;
4084 _line->parent_group().canvas_to_item (cx, cy);
4086 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4091 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
4092 /* no adjacent points */
4096 Drag::start_grab (event, _editor->cursors()->fader);
4098 /* store grab start in parent frame */
4103 double fraction = 1.0 - (cy / _line->height());
4105 _line->start_drag_line (before, after, fraction);
4107 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4111 LineDrag::motion (GdkEvent* event, bool)
4113 double dy = current_pointer_y() - last_pointer_y();
4115 if (event->button.state & Keyboard::SecondaryModifier) {
4119 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4121 _cumulative_y_drag = cy - _fixed_grab_y;
4124 cy = min ((double) _line->height(), cy);
4126 double const fraction = 1.0 - (cy / _line->height());
4129 /* we are ignoring x position for this drag, so we can just pass in anything */
4130 _line->drag_motion (0, fraction, true, false, ignored);
4132 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4136 LineDrag::finished (GdkEvent* event, bool movement_occured)
4138 if (movement_occured) {
4139 motion (event, false);
4140 _line->end_drag (false, 0);
4142 /* add a new control point on the line */
4144 AutomationTimeAxisView* atv;
4146 _line->end_drag (false, 0);
4148 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4149 framepos_t where = _editor->window_event_sample (event, 0, 0);
4150 atv->add_automation_event (event, where, event->button.y, false);
4154 _editor->commit_reversible_command ();
4158 LineDrag::aborted (bool)
4163 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4166 _cumulative_x_drag (0)
4168 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4172 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4174 Drag::start_grab (event);
4176 _line = reinterpret_cast<Line*> (_item);
4179 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4181 double cx = event->button.x;
4182 double cy = event->button.y;
4184 _item->parent()->canvas_to_item (cx, cy);
4186 /* store grab start in parent frame */
4187 _region_view_grab_x = cx;
4189 _before = *(float*) _item->get_data ("position");
4191 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4193 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4197 FeatureLineDrag::motion (GdkEvent*, bool)
4199 double dx = _drags->current_pointer_x() - last_pointer_x();
4201 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4203 _cumulative_x_drag += dx;
4205 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4214 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4216 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4218 float *pos = new float;
4221 _line->set_data ("position", pos);
4227 FeatureLineDrag::finished (GdkEvent*, bool)
4229 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4230 _arv->update_transient(_before, _before);
4234 FeatureLineDrag::aborted (bool)
4239 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4241 , _vertical_only (false)
4243 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4247 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4249 Drag::start_grab (event);
4250 show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4254 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4261 framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4263 framepos_t grab = grab_frame ();
4264 if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4265 _editor->snap_to_with_modifier (grab, event);
4267 grab = raw_grab_frame ();
4270 /* base start and end on initial click position */
4280 if (current_pointer_y() < grab_y()) {
4281 y1 = current_pointer_y();
4284 y2 = current_pointer_y();
4288 if (start != end || y1 != y2) {
4290 double x1 = _editor->sample_to_pixel (start);
4291 double x2 = _editor->sample_to_pixel (end);
4292 const double min_dimension = 2.0;
4294 if (_vertical_only) {
4295 /* fixed 10 pixel width */
4299 x2 = min (x1 - min_dimension, x2);
4301 x2 = max (x1 + min_dimension, x2);
4306 y2 = min (y1 - min_dimension, y2);
4308 y2 = max (y1 + min_dimension, y2);
4311 /* translate rect into item space and set */
4313 ArdourCanvas::Rect r (x1, y1, x2, y2);
4315 /* this drag is a _trackview_only == true drag, so the y1 and
4316 * y2 (computed using current_pointer_y() and grab_y()) will be
4317 * relative to the top of the trackview group). The
4318 * rubberband rect has the same parent/scroll offset as the
4319 * the trackview group, so we can use the "r" rect directly
4320 * to set the shape of the rubberband.
4323 _editor->rubberband_rect->set (r);
4324 _editor->rubberband_rect->show();
4325 _editor->rubberband_rect->raise_to_top();
4327 show_verbose_cursor_time (pf);
4329 do_select_things (event, true);
4334 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4338 framepos_t grab = grab_frame ();
4339 framepos_t lpf = last_pointer_frame ();
4341 if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4342 grab = raw_grab_frame ();
4343 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4357 if (current_pointer_y() < grab_y()) {
4358 y1 = current_pointer_y();
4361 y2 = current_pointer_y();
4365 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4369 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4371 if (movement_occurred) {
4373 motion (event, false);
4374 do_select_things (event, false);
4380 bool do_deselect = true;
4381 MidiTimeAxisView* mtv;
4383 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4385 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4386 /* nothing selected */
4387 add_midi_region (mtv);
4388 do_deselect = false;
4392 /* do not deselect if Primary or Tertiary (toggle-select or
4393 * extend-select are pressed.
4396 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4397 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4404 _editor->rubberband_rect->hide();
4408 RubberbandSelectDrag::aborted (bool)
4410 _editor->rubberband_rect->hide ();
4413 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4414 : RegionDrag (e, i, p, v)
4416 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4420 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4422 Drag::start_grab (event, cursor);
4424 show_verbose_cursor_time (adjusted_current_frame (event));
4428 TimeFXDrag::motion (GdkEvent* event, bool)
4430 RegionView* rv = _primary;
4431 StreamView* cv = rv->get_time_axis_view().view ();
4433 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4434 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4435 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4437 framepos_t const pf = adjusted_current_frame (event);
4439 if (pf > rv->region()->position()) {
4440 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4443 show_verbose_cursor_time (pf);
4447 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4449 _primary->get_time_axis_view().hide_timestretch ();
4451 if (!movement_occurred) {
4455 if (last_pointer_frame() < _primary->region()->position()) {
4456 /* backwards drag of the left edge - not usable */
4460 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4462 float percentage = (double) newlen / (double) _primary->region()->length();
4464 #ifndef USE_RUBBERBAND
4465 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4466 if (_primary->region()->data_type() == DataType::AUDIO) {
4467 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4471 if (!_editor->get_selection().regions.empty()) {
4472 /* primary will already be included in the selection, and edit
4473 group shared editing will propagate selection across
4474 equivalent regions, so just use the current region
4478 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4479 error << _("An error occurred while executing time stretch operation") << endmsg;
4485 TimeFXDrag::aborted (bool)
4487 _primary->get_time_axis_view().hide_timestretch ();
4490 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4493 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4497 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4499 Drag::start_grab (event);
4503 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4505 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4509 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4511 if (movement_occurred && _editor->session()) {
4512 /* make sure we stop */
4513 _editor->session()->request_transport_speed (0.0);
4518 ScrubDrag::aborted (bool)
4523 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4527 , _time_selection_at_start (!_editor->get_selection().time.empty())
4529 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4531 if (_time_selection_at_start) {
4532 start_at_start = _editor->get_selection().time.start();
4533 end_at_start = _editor->get_selection().time.end_frame();
4538 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4540 if (_editor->session() == 0) {
4544 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4546 switch (_operation) {
4547 case CreateSelection:
4548 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4553 cursor = _editor->cursors()->selector;
4554 Drag::start_grab (event, cursor);
4557 case SelectionStartTrim:
4558 if (_editor->clicked_axisview) {
4559 _editor->clicked_axisview->order_selection_trims (_item, true);
4561 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4564 case SelectionEndTrim:
4565 if (_editor->clicked_axisview) {
4566 _editor->clicked_axisview->order_selection_trims (_item, false);
4568 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4572 Drag::start_grab (event, cursor);
4575 case SelectionExtend:
4576 Drag::start_grab (event, cursor);
4580 if (_operation == SelectionMove) {
4581 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4583 show_verbose_cursor_time (adjusted_current_frame (event));
4588 SelectionDrag::setup_pointer_frame_offset ()
4590 switch (_operation) {
4591 case CreateSelection:
4592 _pointer_frame_offset = 0;
4595 case SelectionStartTrim:
4597 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4600 case SelectionEndTrim:
4601 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4604 case SelectionExtend:
4610 SelectionDrag::motion (GdkEvent* event, bool first_move)
4612 framepos_t start = 0;
4614 framecnt_t length = 0;
4615 framecnt_t distance = 0;
4617 framepos_t const pending_position = adjusted_current_frame (event);
4619 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4623 switch (_operation) {
4624 case CreateSelection:
4626 framepos_t grab = grab_frame ();
4629 grab = adjusted_current_frame (event, false);
4630 if (grab < pending_position) {
4631 _editor->snap_to (grab, RoundDownMaybe);
4633 _editor->snap_to (grab, RoundUpMaybe);
4637 if (pending_position < grab) {
4638 start = pending_position;
4641 end = pending_position;
4645 /* first drag: Either add to the selection
4646 or create a new selection
4653 /* adding to the selection */
4654 _editor->set_selected_track_as_side_effect (Selection::Add);
4655 _editor->clicked_selection = _editor->selection->add (start, end);
4662 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4663 _editor->set_selected_track_as_side_effect (Selection::Set);
4666 _editor->clicked_selection = _editor->selection->set (start, end);
4670 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4671 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4672 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4674 _editor->selection->add (atest);
4678 /* select all tracks within the rectangle that we've marked out so far */
4679 TrackViewList new_selection;
4680 TrackViewList& all_tracks (_editor->track_views);
4682 ArdourCanvas::Coord const top = grab_y();
4683 ArdourCanvas::Coord const bottom = current_pointer_y();
4685 if (top >= 0 && bottom >= 0) {
4687 //first, find the tracks that are covered in the y range selection
4688 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4689 if ((*i)->covered_by_y_range (top, bottom)) {
4690 new_selection.push_back (*i);
4694 //now find any tracks that are GROUPED with the tracks we selected
4695 TrackViewList grouped_add = new_selection;
4696 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4697 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4698 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4699 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4700 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4701 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4702 grouped_add.push_back (*j);
4707 //now compare our list with the current selection, and add or remove as necessary
4708 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4709 TrackViewList tracks_to_add;
4710 TrackViewList tracks_to_remove;
4711 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4712 if ( !_editor->selection->tracks.contains ( *i ) )
4713 tracks_to_add.push_back ( *i );
4714 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4715 if ( !grouped_add.contains ( *i ) )
4716 tracks_to_remove.push_back ( *i );
4717 _editor->selection->add(tracks_to_add);
4718 _editor->selection->remove(tracks_to_remove);
4724 case SelectionStartTrim:
4726 start = _editor->selection->time[_editor->clicked_selection].start;
4727 end = _editor->selection->time[_editor->clicked_selection].end;
4729 if (pending_position > end) {
4732 start = pending_position;
4736 case SelectionEndTrim:
4738 start = _editor->selection->time[_editor->clicked_selection].start;
4739 end = _editor->selection->time[_editor->clicked_selection].end;
4741 if (pending_position < start) {
4744 end = pending_position;
4751 start = _editor->selection->time[_editor->clicked_selection].start;
4752 end = _editor->selection->time[_editor->clicked_selection].end;
4754 length = end - start;
4755 distance = pending_position - start;
4756 start = pending_position;
4757 _editor->snap_to (start);
4759 end = start + length;
4763 case SelectionExtend:
4768 switch (_operation) {
4770 if (_time_selection_at_start) {
4771 _editor->selection->move_time (distance);
4775 _editor->selection->replace (_editor->clicked_selection, start, end);
4779 if (_operation == SelectionMove) {
4780 show_verbose_cursor_time(start);
4782 show_verbose_cursor_time(pending_position);
4787 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4789 Session* s = _editor->session();
4791 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4792 if (movement_occurred) {
4793 motion (event, false);
4794 /* XXX this is not object-oriented programming at all. ick */
4795 if (_editor->selection->time.consolidate()) {
4796 _editor->selection->TimeChanged ();
4799 /* XXX what if its a music time selection? */
4801 if ( s->get_play_range() && s->transport_rolling() ) {
4802 s->request_play_range (&_editor->selection->time, true);
4804 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4805 if (_operation == SelectionEndTrim)
4806 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4808 s->request_locate (_editor->get_selection().time.start());
4814 /* just a click, no pointer movement.
4817 if (_operation == SelectionExtend) {
4818 if (_time_selection_at_start) {
4819 framepos_t pos = adjusted_current_frame (event, false);
4820 framepos_t start = min (pos, start_at_start);
4821 framepos_t end = max (pos, end_at_start);
4822 _editor->selection->set (start, end);
4825 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4826 if (_editor->clicked_selection) {
4827 _editor->selection->remove (_editor->clicked_selection);
4830 if (!_editor->clicked_selection) {
4831 _editor->selection->clear_time();
4836 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4837 _editor->selection->set (_editor->clicked_axisview);
4840 if (s && s->get_play_range () && s->transport_rolling()) {
4841 s->request_stop (false, false);
4846 _editor->stop_canvas_autoscroll ();
4847 _editor->clicked_selection = 0;
4848 _editor->commit_reversible_selection_op ();
4852 SelectionDrag::aborted (bool)
4857 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4858 : Drag (e, i, false),
4862 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4864 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4865 ArdourCanvas::Rect (0.0, 0.0, 0.0,
4866 physical_screen_height (_editor->get_window())));
4867 _drag_rect->hide ();
4869 _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4870 _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4873 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4875 /* normal canvas items will be cleaned up when their parent group is deleted. But
4876 this item is created as the child of a long-lived parent group, and so we
4877 need to explicitly delete it.
4883 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4885 if (_editor->session() == 0) {
4889 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4891 if (!_editor->temp_location) {
4892 _editor->temp_location = new Location (*_editor->session());
4895 switch (_operation) {
4896 case CreateSkipMarker:
4897 case CreateRangeMarker:
4898 case CreateTransportMarker:
4899 case CreateCDMarker:
4901 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4906 cursor = _editor->cursors()->selector;
4910 Drag::start_grab (event, cursor);
4912 show_verbose_cursor_time (adjusted_current_frame (event));
4916 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4918 framepos_t start = 0;
4920 ArdourCanvas::Rectangle *crect;
4922 switch (_operation) {
4923 case CreateSkipMarker:
4924 crect = _editor->range_bar_drag_rect;
4926 case CreateRangeMarker:
4927 crect = _editor->range_bar_drag_rect;
4929 case CreateTransportMarker:
4930 crect = _editor->transport_bar_drag_rect;
4932 case CreateCDMarker:
4933 crect = _editor->cd_marker_bar_drag_rect;
4936 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4941 framepos_t const pf = adjusted_current_frame (event);
4943 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4944 framepos_t grab = grab_frame ();
4945 _editor->snap_to (grab);
4947 if (pf < grab_frame()) {
4955 /* first drag: Either add to the selection
4956 or create a new selection.
4961 _editor->temp_location->set (start, end);
4965 update_item (_editor->temp_location);
4967 //_drag_rect->raise_to_top();
4973 _editor->temp_location->set (start, end);
4975 double x1 = _editor->sample_to_pixel (start);
4976 double x2 = _editor->sample_to_pixel (end);
4980 update_item (_editor->temp_location);
4983 show_verbose_cursor_time (pf);
4988 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4990 Location * newloc = 0;
4994 if (movement_occurred) {
4995 motion (event, false);
4998 switch (_operation) {
4999 case CreateSkipMarker:
5000 case CreateRangeMarker:
5001 case CreateCDMarker:
5003 XMLNode &before = _editor->session()->locations()->get_state();
5004 if (_operation == CreateSkipMarker) {
5005 _editor->begin_reversible_command (_("new skip marker"));
5006 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5007 flags = Location::IsRangeMarker | Location::IsSkip;
5008 _editor->range_bar_drag_rect->hide();
5009 } else if (_operation == CreateCDMarker) {
5010 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5011 _editor->begin_reversible_command (_("new CD marker"));
5012 flags = Location::IsRangeMarker | Location::IsCDMarker;
5013 _editor->cd_marker_bar_drag_rect->hide();
5015 _editor->begin_reversible_command (_("new skip marker"));
5016 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5017 flags = Location::IsRangeMarker;
5018 _editor->range_bar_drag_rect->hide();
5020 newloc = new Location (
5021 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5024 _editor->session()->locations()->add (newloc, true);
5025 XMLNode &after = _editor->session()->locations()->get_state();
5026 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5027 _editor->commit_reversible_command ();
5031 case CreateTransportMarker:
5032 // popup menu to pick loop or punch
5033 _editor->new_transport_marker_context_menu (&event->button, _item);
5039 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5041 if (_operation == CreateTransportMarker) {
5043 /* didn't drag, so just locate */
5045 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5047 } else if (_operation == CreateCDMarker) {
5049 /* didn't drag, but mark is already created so do
5052 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5057 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5059 if (end == max_framepos) {
5060 end = _editor->session()->current_end_frame ();
5063 if (start == max_framepos) {
5064 start = _editor->session()->current_start_frame ();
5067 switch (_editor->mouse_mode) {
5069 /* find the two markers on either side and then make the selection from it */
5070 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5074 /* find the two markers on either side of the click and make the range out of it */
5075 _editor->selection->set (start, end);
5084 _editor->stop_canvas_autoscroll ();
5088 RangeMarkerBarDrag::aborted (bool movement_occured)
5090 if (movement_occured) {
5091 _drag_rect->hide ();
5096 RangeMarkerBarDrag::update_item (Location* location)
5098 double const x1 = _editor->sample_to_pixel (location->start());
5099 double const x2 = _editor->sample_to_pixel (location->end());
5101 _drag_rect->set_x0 (x1);
5102 _drag_rect->set_x1 (x2);
5105 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5107 , _cumulative_dx (0)
5108 , _cumulative_dy (0)
5110 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5112 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5114 _region = &_primary->region_view ();
5115 _note_height = _region->midi_stream_view()->note_height ();
5119 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5121 Drag::start_grab (event);
5123 if (!(_was_selected = _primary->selected())) {
5125 /* tertiary-click means extend selection - we'll do that on button release,
5126 so don't add it here, because otherwise we make it hard to figure
5127 out the "extend-to" range.
5130 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5133 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5136 _region->note_selected (_primary, true);
5138 _region->unique_select (_primary);
5141 _editor->begin_reversible_selection_op(X_("Select Note Press"));
5142 _editor->commit_reversible_selection_op();
5147 /** @return Current total drag x change in frames */
5149 NoteDrag::total_dx () const
5152 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5154 /* primary note time */
5155 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5157 /* new time of the primary note in session frames */
5158 frameoffset_t st = n + dx;
5160 framepos_t const rp = _region->region()->position ();
5162 /* prevent the note being dragged earlier than the region's position */
5165 /* snap and return corresponding delta */
5166 return _region->snap_frame_to_frame (st - rp) + rp - n;
5169 /** @return Current total drag y change in note number */
5171 NoteDrag::total_dy () const
5173 MidiStreamView* msv = _region->midi_stream_view ();
5174 double const y = _region->midi_view()->y_position ();
5175 /* new current note */
5176 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5178 n = max (msv->lowest_note(), n);
5179 n = min (msv->highest_note(), n);
5180 /* and work out delta */
5181 return n - msv->y_to_note (grab_y() - y);
5185 NoteDrag::motion (GdkEvent *, bool)
5187 /* Total change in x and y since the start of the drag */
5188 frameoffset_t const dx = total_dx ();
5189 int8_t const dy = total_dy ();
5191 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5192 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5193 double const tdy = -dy * _note_height - _cumulative_dy;
5196 _cumulative_dx += tdx;
5197 _cumulative_dy += tdy;
5199 int8_t note_delta = total_dy();
5201 _region->move_selection (tdx, tdy, note_delta);
5203 /* the new note value may be the same as the old one, but we
5204 * don't know what that means because the selection may have
5205 * involved more than one note and we might be doing something
5206 * odd with them. so show the note value anyway, always.
5210 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5212 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5213 (int) floor ((double)new_note));
5215 show_verbose_cursor_text (buf);
5220 NoteDrag::finished (GdkEvent* ev, bool moved)
5223 /* no motion - select note */
5225 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5226 _editor->current_mouse_mode() == Editing::MouseDraw) {
5228 bool changed = false;
5230 if (_was_selected) {
5231 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5233 _region->note_deselected (_primary);
5237 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5238 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5240 if (!extend && !add && _region->selection_size() > 1) {
5241 _region->unique_select (_primary);
5243 } else if (extend) {
5244 _region->note_selected (_primary, true, true);
5247 /* it was added during button press */
5252 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5253 _editor->commit_reversible_selection_op();
5257 _region->note_dropped (_primary, total_dx(), total_dy());
5262 NoteDrag::aborted (bool)
5267 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5268 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5269 : Drag (editor, atv->base_item ())
5271 , _y_origin (atv->y_position())
5272 , _nothing_to_drag (false)
5274 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5275 setup (atv->lines ());
5278 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5279 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5280 : Drag (editor, rv->get_canvas_group ())
5282 , _y_origin (rv->get_time_axis_view().y_position())
5283 , _nothing_to_drag (false)
5286 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5288 list<boost::shared_ptr<AutomationLine> > lines;
5290 AudioRegionView* audio_view;
5291 AutomationRegionView* automation_view;
5292 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5293 lines.push_back (audio_view->get_gain_line ());
5294 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5295 lines.push_back (automation_view->line ());
5298 error << _("Automation range drag created for invalid region type") << endmsg;
5304 /** @param lines AutomationLines to drag.
5305 * @param offset Offset from the session start to the points in the AutomationLines.
5308 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5310 /* find the lines that overlap the ranges being dragged */
5311 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5312 while (i != lines.end ()) {
5313 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5316 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5318 /* check this range against all the AudioRanges that we are using */
5319 list<AudioRange>::const_iterator k = _ranges.begin ();
5320 while (k != _ranges.end()) {
5321 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5327 /* add it to our list if it overlaps at all */
5328 if (k != _ranges.end()) {
5333 _lines.push_back (n);
5339 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5343 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5345 return 1.0 - ((global_y - _y_origin) / line->height());
5349 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5351 const double v = list->eval(x);
5352 return _integral ? rint(v) : v;
5356 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5358 Drag::start_grab (event, cursor);
5360 /* Get line states before we start changing things */
5361 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5362 i->state = &i->line->get_state ();
5363 i->original_fraction = y_fraction (i->line, current_pointer_y());
5366 if (_ranges.empty()) {
5368 /* No selected time ranges: drag all points */
5369 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5370 uint32_t const N = i->line->npoints ();
5371 for (uint32_t j = 0; j < N; ++j) {
5372 i->points.push_back (i->line->nth (j));
5378 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5380 framecnt_t const half = (i->start + i->end) / 2;
5382 /* find the line that this audio range starts in */
5383 list<Line>::iterator j = _lines.begin();
5384 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5388 if (j != _lines.end()) {
5389 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5391 /* j is the line that this audio range starts in; fade into it;
5392 64 samples length plucked out of thin air.
5395 framepos_t a = i->start + 64;
5400 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5401 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5403 the_list->editor_add (p, value (the_list, p));
5404 the_list->editor_add (q, value (the_list, q));
5407 /* same thing for the end */
5410 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5414 if (j != _lines.end()) {
5415 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5417 /* j is the line that this audio range starts in; fade out of it;
5418 64 samples length plucked out of thin air.
5421 framepos_t b = i->end - 64;
5426 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5427 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5429 the_list->editor_add (p, value (the_list, p));
5430 the_list->editor_add (q, value (the_list, q));
5434 _nothing_to_drag = true;
5436 /* Find all the points that should be dragged and put them in the relevant
5437 points lists in the Line structs.
5440 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5442 uint32_t const N = i->line->npoints ();
5443 for (uint32_t j = 0; j < N; ++j) {
5445 /* here's a control point on this line */
5446 ControlPoint* p = i->line->nth (j);
5447 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5449 /* see if it's inside a range */
5450 list<AudioRange>::const_iterator k = _ranges.begin ();
5451 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5455 if (k != _ranges.end()) {
5456 /* dragging this point */
5457 _nothing_to_drag = false;
5458 i->points.push_back (p);
5464 if (_nothing_to_drag) {
5468 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5469 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5474 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5476 if (_nothing_to_drag) {
5480 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5481 float const f = y_fraction (l->line, current_pointer_y());
5482 /* we are ignoring x position for this drag, so we can just pass in anything */
5484 l->line->drag_motion (0, f, true, false, ignored);
5485 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5490 AutomationRangeDrag::finished (GdkEvent* event, bool)
5492 if (_nothing_to_drag) {
5496 motion (event, false);
5497 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5498 i->line->end_drag (false, 0);
5501 _editor->commit_reversible_command ();
5505 AutomationRangeDrag::aborted (bool)
5507 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5512 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5514 , initial_time_axis_view (itav)
5516 /* note that time_axis_view may be null if the regionview was created
5517 * as part of a copy operation.
5519 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5520 layer = v->region()->layer ();
5521 initial_y = v->get_canvas_group()->position().y;
5522 initial_playlist = v->region()->playlist ();
5523 initial_position = v->region()->position ();
5524 initial_end = v->region()->position () + v->region()->length ();
5527 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5528 : Drag (e, i->canvas_item ())
5531 , _cumulative_dx (0)
5533 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5534 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5539 PatchChangeDrag::motion (GdkEvent* ev, bool)
5541 framepos_t f = adjusted_current_frame (ev);
5542 boost::shared_ptr<Region> r = _region_view->region ();
5543 f = max (f, r->position ());
5544 f = min (f, r->last_frame ());
5546 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5547 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5548 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5549 _cumulative_dx = dxu;
5553 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5555 if (!movement_occurred) {
5559 boost::shared_ptr<Region> r (_region_view->region ());
5560 framepos_t f = adjusted_current_frame (ev);
5561 f = max (f, r->position ());
5562 f = min (f, r->last_frame ());
5564 _region_view->move_patch_change (
5566 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5571 PatchChangeDrag::aborted (bool)
5573 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5577 PatchChangeDrag::setup_pointer_frame_offset ()
5579 boost::shared_ptr<Region> region = _region_view->region ();
5580 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5583 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5584 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5591 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5593 _region_view->update_drag_selection (
5595 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5599 MidiRubberbandSelectDrag::deselect_things ()
5604 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5605 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5608 _vertical_only = true;
5612 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5614 double const y = _region_view->midi_view()->y_position ();
5616 y1 = max (0.0, y1 - y);
5617 y2 = max (0.0, y2 - y);
5619 _region_view->update_vertical_drag_selection (
5622 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5627 MidiVerticalSelectDrag::deselect_things ()
5632 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5633 : RubberbandSelectDrag (e, i)
5639 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5641 if (drag_in_progress) {
5642 /* We just want to select things at the end of the drag, not during it */
5646 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5648 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5650 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5652 _editor->commit_reversible_selection_op ();
5656 EditorRubberbandSelectDrag::deselect_things ()
5658 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5660 _editor->selection->clear_tracks();
5661 _editor->selection->clear_regions();
5662 _editor->selection->clear_points ();
5663 _editor->selection->clear_lines ();
5664 _editor->selection->clear_midi_notes ();
5666 _editor->commit_reversible_selection_op();
5669 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5674 _note[0] = _note[1] = 0;
5677 NoteCreateDrag::~NoteCreateDrag ()
5683 NoteCreateDrag::grid_frames (framepos_t t) const
5686 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5688 grid_beats = Evoral::Beats(1);
5691 return _region_view->region_beats_to_region_frames (grid_beats);
5695 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5697 Drag::start_grab (event, cursor);
5699 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5701 framepos_t pf = _drags->current_pointer_frame ();
5702 framecnt_t const g = grid_frames (pf);
5704 /* Hack so that we always snap to the note that we are over, instead of snapping
5705 to the next one if we're more than halfway through the one we're over.
5707 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5711 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5712 _note[1] = _note[0];
5714 MidiStreamView* sv = _region_view->midi_stream_view ();
5715 double const x = _editor->sample_to_pixel (_note[0]);
5716 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5718 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5719 _drag_rect->set_outline_all ();
5720 _drag_rect->set_outline_color (0xffffff99);
5721 _drag_rect->set_fill_color (0xffffff66);
5725 NoteCreateDrag::motion (GdkEvent* event, bool)
5727 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5728 double const x0 = _editor->sample_to_pixel (_note[0]);
5729 double const x1 = _editor->sample_to_pixel (_note[1]);
5730 _drag_rect->set_x0 (std::min(x0, x1));
5731 _drag_rect->set_x1 (std::max(x0, x1));
5735 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5737 if (!had_movement) {
5741 framepos_t const start = min (_note[0], _note[1]);
5742 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5744 framecnt_t const g = grid_frames (start);
5745 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5747 if (_editor->snap_mode() == SnapNormal && length < g) {
5751 Evoral::Beats length_beats = max (
5752 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5754 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5758 NoteCreateDrag::y_to_region (double y) const
5761 _region_view->get_canvas_group()->canvas_to_item (x, y);
5766 NoteCreateDrag::aborted (bool)
5771 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5776 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
5780 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5782 Drag::start_grab (event, cursor);
5786 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5792 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5795 distance = _drags->current_pointer_x() - grab_x();
5796 len = ar->fade_in()->back()->when;
5798 distance = grab_x() - _drags->current_pointer_x();
5799 len = ar->fade_out()->back()->when;
5802 /* how long should it be ? */
5804 new_length = len + _editor->pixel_to_sample (distance);
5806 /* now check with the region that this is legal */
5808 new_length = ar->verify_xfade_bounds (new_length, start);
5811 arv->reset_fade_in_shape_width (ar, new_length);
5813 arv->reset_fade_out_shape_width (ar, new_length);
5818 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5824 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5827 distance = _drags->current_pointer_x() - grab_x();
5828 len = ar->fade_in()->back()->when;
5830 distance = grab_x() - _drags->current_pointer_x();
5831 len = ar->fade_out()->back()->when;
5834 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5836 _editor->begin_reversible_command ("xfade trim");
5837 ar->playlist()->clear_owned_changes ();
5840 ar->set_fade_in_length (new_length);
5842 ar->set_fade_out_length (new_length);
5845 /* Adjusting the xfade may affect other regions in the playlist, so we need
5846 to get undo Commands from the whole playlist rather than just the
5850 vector<Command*> cmds;
5851 ar->playlist()->rdiff (cmds);
5852 _editor->session()->add_commands (cmds);
5853 _editor->commit_reversible_command ();
5858 CrossfadeEdgeDrag::aborted (bool)
5861 // arv->redraw_start_xfade ();
5863 // arv->redraw_end_xfade ();
5867 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5868 : Drag (e, item, true)
5869 , line (new EditorCursor (*e))
5871 line->set_position (pos);
5875 RegionCutDrag::~RegionCutDrag ()
5881 RegionCutDrag::motion (GdkEvent*, bool)
5883 framepos_t where = _drags->current_pointer_frame();
5884 _editor->snap_to (where);
5886 line->set_position (where);
5890 RegionCutDrag::finished (GdkEvent*, bool)
5892 _editor->get_track_canvas()->canvas()->re_enter();
5894 framepos_t pos = _drags->current_pointer_frame();
5898 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5904 _editor->split_regions_at (pos, rs);
5908 RegionCutDrag::aborted (bool)