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 "ui_config.h"
68 #include "verbose_cursor.h"
71 using namespace ARDOUR;
74 using namespace Gtkmm2ext;
75 using namespace Editing;
76 using namespace ArdourCanvas;
78 using Gtkmm2ext::Keyboard;
80 double ControlPointDrag::_zero_gain_fraction = -1.0;
82 DragManager::DragManager (Editor* e)
85 , _current_pointer_x (0.0)
86 , _current_pointer_y (0.0)
87 , _current_pointer_frame (0)
88 , _old_follow_playhead (false)
92 DragManager::~DragManager ()
97 /** Call abort for each active drag */
103 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
108 if (!_drags.empty ()) {
109 _editor->set_follow_playhead (_old_follow_playhead, false);
113 _editor->abort_reversible_command();
119 DragManager::add (Drag* d)
121 d->set_manager (this);
122 _drags.push_back (d);
126 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
128 d->set_manager (this);
129 _drags.push_back (d);
134 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
136 /* Prevent follow playhead during the drag to be nice to the user */
137 _old_follow_playhead = _editor->follow_playhead ();
138 _editor->set_follow_playhead (false);
140 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
142 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
143 (*i)->start_grab (e, c);
147 /** Call end_grab for each active drag.
148 * @return true if any drag reported movement having occurred.
151 DragManager::end_grab (GdkEvent* e)
156 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
157 bool const t = (*i)->end_grab (e);
168 _editor->set_follow_playhead (_old_follow_playhead, false);
174 DragManager::mark_double_click ()
176 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
177 (*i)->set_double_click (true);
182 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
186 /* calling this implies that we expect the event to have canvas
189 * Can we guarantee that this is true?
192 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
194 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
195 bool const t = (*i)->motion_handler (e, from_autoscroll);
196 /* run all handlers; return true if at least one of them
197 returns true (indicating that the event has been handled).
209 DragManager::have_item (ArdourCanvas::Item* i) const
211 list<Drag*>::const_iterator j = _drags.begin ();
212 while (j != _drags.end() && (*j)->item () != i) {
216 return j != _drags.end ();
219 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
223 , _pointer_frame_offset (0)
224 , _x_constrained (false)
225 , _y_constrained (false)
226 , _was_rolling (false)
227 , _trackview_only (trackview_only)
228 , _move_threshold_passed (false)
229 , _starting_point_passed (false)
230 , _initially_vertical (false)
231 , _was_double_click (false)
234 , _last_pointer_x (0.0)
235 , _last_pointer_y (0.0)
236 , _raw_grab_frame (0)
238 , _last_pointer_frame (0)
245 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
251 _cursor_ctx = CursorContext::create (*_editor, cursor);
253 _cursor_ctx->change (cursor);
260 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
263 /* we set up x/y dragging constraints on first move */
265 _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::snap_delta (guint state) const
349 if (ArdourKeyboard::indicates_snap_delta (state)) {
357 Drag::current_pointer_x() const
359 return _drags->current_pointer_x ();
363 Drag::current_pointer_y () const
365 if (!_trackview_only) {
366 return _drags->current_pointer_y ();
369 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
373 Drag::setup_snap_delta (framepos_t pos)
375 framepos_t temp = pos;
376 _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
377 _snap_delta = temp - pos;
381 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
383 /* check to see if we have moved in any way that matters since the last motion event */
384 if (_move_threshold_passed &&
385 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
386 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
390 pair<framecnt_t, int> const threshold = move_threshold ();
392 bool const old_move_threshold_passed = _move_threshold_passed;
394 if (!_move_threshold_passed) {
396 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
397 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
399 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
402 if (active (_editor->mouse_mode) && _move_threshold_passed) {
404 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
406 if (old_move_threshold_passed != _move_threshold_passed) {
410 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
411 _initially_vertical = true;
413 _initially_vertical = false;
415 /** check constraints for this drag.
416 * Note that the current convention is to use "contains" for
417 * key modifiers during motion and "equals" when initiating a drag.
418 * In this case we haven't moved yet, so "equals" applies here.
420 if (Config->get_edit_mode() != Lock) {
421 if (event->motion.state & Gdk::BUTTON2_MASK) {
422 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
423 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
424 _x_constrained = false;
425 _y_constrained = true;
427 _x_constrained = true;
428 _y_constrained = false;
430 } else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
431 // if dragging normally, the motion is constrained to the first direction of movement.
432 if (_initially_vertical) {
433 _x_constrained = true;
434 _y_constrained = false;
436 _x_constrained = false;
437 _y_constrained = true;
441 if (event->button.state & Gdk::BUTTON2_MASK) {
442 _x_constrained = false;
444 _x_constrained = true;
446 _y_constrained = false;
450 if (!from_autoscroll) {
451 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
454 if (!_editor->autoscroll_active() || from_autoscroll) {
457 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
459 motion (event, first_move && !_starting_point_passed);
461 if (first_move && !_starting_point_passed) {
462 _starting_point_passed = true;
465 _last_pointer_x = _drags->current_pointer_x ();
466 _last_pointer_y = current_pointer_y ();
467 _last_pointer_frame = adjusted_current_frame (event, false);
477 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
485 aborted (_move_threshold_passed);
487 _editor->stop_canvas_autoscroll ();
488 _editor->verbose_cursor()->hide ();
492 Drag::show_verbose_cursor_time (framepos_t frame)
494 _editor->verbose_cursor()->set_time (frame);
495 _editor->verbose_cursor()->show ();
499 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
501 _editor->verbose_cursor()->set_duration (start, end);
502 _editor->verbose_cursor()->show ();
506 Drag::show_verbose_cursor_text (string const & text)
508 _editor->verbose_cursor()->set (text);
509 _editor->verbose_cursor()->show ();
512 boost::shared_ptr<Region>
513 Drag::add_midi_region (MidiTimeAxisView* view, bool commit)
515 if (_editor->session()) {
516 const TempoMap& map (_editor->session()->tempo_map());
517 framecnt_t pos = grab_frame();
518 const Meter& m = map.meter_at (pos);
519 /* not that the frame rate used here can be affected by pull up/down which
522 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
523 return view->add_region (grab_frame(), len, commit);
526 return boost::shared_ptr<Region>();
529 struct EditorOrderTimeAxisViewSorter {
530 bool operator() (TimeAxisView* a, TimeAxisView* b) {
531 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
532 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
534 return ra->route()->order_key () < rb->route()->order_key ();
538 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
543 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
545 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
546 as some of the regions we are dragging may be on such tracks.
549 TrackViewList track_views = _editor->track_views;
550 track_views.sort (EditorOrderTimeAxisViewSorter ());
552 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
553 _time_axis_views.push_back (*i);
555 TimeAxisView::Children children_list = (*i)->get_child_list ();
556 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
557 _time_axis_views.push_back (j->get());
561 /* the list of views can be empty at this point if this is a region list-insert drag
564 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
565 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
568 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
572 RegionDrag::region_going_away (RegionView* v)
574 list<DraggingView>::iterator i = _views.begin ();
575 while (i != _views.end() && i->view != v) {
579 if (i != _views.end()) {
584 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
585 * or -1 if it is not found.
588 RegionDrag::find_time_axis_view (TimeAxisView* t) const
591 int const N = _time_axis_views.size ();
592 while (i < N && _time_axis_views[i] != t) {
596 if (_time_axis_views[i] != t) {
603 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
604 : RegionDrag (e, i, p, v)
606 , _ignore_video_lock (false)
608 , _last_pointer_time_axis_view (0)
609 , _last_pointer_layer (0)
614 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
618 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
620 Drag::start_grab (event, cursor);
621 setup_snap_delta (_last_frame_position);
623 show_verbose_cursor_time (_last_frame_position);
625 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
627 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
628 assert(_last_pointer_time_axis_view >= 0);
629 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
632 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
633 _ignore_video_lock = true;
637 /* cross track dragging seems broken here. disabled for now. */
638 _y_constrained = true;
643 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
645 /* compute the amount of pointer motion in frames, and where
646 the region would be if we moved it by that much.
648 *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
650 framepos_t sync_frame;
651 framecnt_t sync_offset;
654 sync_offset = _primary->region()->sync_offset (sync_dir);
656 /* we don't handle a sync point that lies before zero.
658 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
660 framecnt_t const sd = snap_delta (event->button.state);
661 sync_frame = *pending_region_position + (sync_dir * sync_offset) + sd;
663 _editor->snap_to_with_modifier (sync_frame, event);
665 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - sd;
668 *pending_region_position = _last_frame_position;
671 if (*pending_region_position > max_framepos - _primary->region()->length()) {
672 *pending_region_position = _last_frame_position;
677 bool const x_move_allowed = !_x_constrained;
679 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
681 /* x movement since last time (in pixels) */
682 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
684 /* total x movement */
685 framecnt_t total_dx = *pending_region_position;
686 if (regions_came_from_canvas()) {
687 total_dx = total_dx - grab_frame ();
690 /* check that no regions have gone off the start of the session */
691 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
692 if ((i->view->region()->position() + total_dx) < 0) {
694 *pending_region_position = _last_frame_position;
705 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
711 const int tavsize = _time_axis_views.size();
712 const int dt = delta > 0 ? +1 : -1;
714 int target = start + delta - skip;
716 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
717 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
719 while (current >= 0 && current != target) {
721 if (current < 0 && dt < 0) {
724 if (current >= tavsize && dt > 0) {
727 if (current < 0 || current >= tavsize) {
731 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
732 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
736 if (distance_only && current == start + delta) {
744 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
746 if (_y_constrained) {
750 const int tavsize = _time_axis_views.size();
751 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
752 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
753 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
755 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
756 /* already in the drop zone */
757 if (delta_track >= 0) {
758 /* downward motion - OK if others are still not in the dropzone */
767 } else if (n >= tavsize) {
768 /* downward motion into drop zone. That's fine. */
772 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
773 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
774 /* not a track, or the wrong type */
778 double const l = i->layer + delta_layer;
780 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
781 mode to allow the user to place a region below another on layer 0.
783 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
784 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
785 If it has, the layers will be munged later anyway, so it's ok.
791 /* all regions being dragged are ok with this change */
795 struct DraggingViewSorter {
796 bool operator() (const DraggingView& a, const DraggingView& b) {
797 return a.time_axis_view < b.time_axis_view;
802 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
804 double delta_layer = 0;
805 int delta_time_axis_view = 0;
806 int current_pointer_time_axis_view = -1;
808 assert (!_views.empty ());
810 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
812 /* Find the TimeAxisView that the pointer is now over */
813 const double cur_y = current_pointer_y ();
814 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
815 TimeAxisView* tv = r.first;
817 if (!tv && cur_y < 0) {
818 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
822 /* find drop-zone y-position */
823 Coord last_track_bottom_edge;
824 last_track_bottom_edge = 0;
825 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
826 if (!(*t)->hidden()) {
827 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
832 if (tv && tv->view()) {
833 /* the mouse is over a track */
834 double layer = r.second;
836 if (first_move && tv->view()->layer_display() == Stacked) {
837 tv->view()->set_layer_display (Expanded);
840 /* Here's the current pointer position in terms of time axis view and layer */
841 current_pointer_time_axis_view = find_time_axis_view (tv);
842 assert(current_pointer_time_axis_view >= 0);
844 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
846 /* Work out the change in y */
848 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
849 if (!rtv || !rtv->is_track()) {
850 /* ignore busses early on. we can't move any regions on them */
851 } else if (_last_pointer_time_axis_view < 0) {
852 /* Was in the drop-zone, now over a track.
853 * Hence it must be an upward move (from the bottom)
855 * track_index is still -1, so delta must be set to
856 * move up the correct number of tracks from the bottom.
858 * This is necessary because steps may be skipped if
859 * the bottom-most track is not a valid target and/or
860 * if there are hidden tracks at the bottom.
861 * Hence the initial offset (_ddropzone) as well as the
862 * last valid pointer position (_pdropzone) need to be
863 * taken into account.
865 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
867 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
870 /* TODO needs adjustment per DraggingView,
872 * e.g. select one region on the top-layer of a track
873 * and one region which is at the bottom-layer of another track
876 * Indicated drop-zones and layering is wrong.
877 * and may infer additional layers on the target-track
878 * (depending how many layers the original track had).
880 * Or select two regions (different layers) on a same track,
881 * move across a non-layer track.. -> layering info is lost.
882 * on drop either of the regions may be on top.
884 * Proposed solution: screw it :) well,
885 * don't use delta_layer, use an absolute value
886 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
887 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
888 * 3) iterate over all DraggingView, find the one that is over the track with most layers
889 * 4) proportionally scale layer to layers available on target
891 delta_layer = current_pointer_layer - _last_pointer_layer;
894 /* for automation lanes, there is a TimeAxisView but no ->view()
895 * if (!tv) -> dropzone
897 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
898 /* Moving into the drop-zone.. */
899 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
900 /* delta_time_axis_view may not be sufficient to move into the DZ
901 * the mouse may enter it, but it may not be a valid move due to
904 * -> remember the delta needed to move into the dropzone
906 _ddropzone = delta_time_axis_view;
907 /* ..but subtract hidden tracks (or routes) at the bottom.
908 * we silently move mover them
910 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
911 - _time_axis_views.size();
913 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
914 /* move around inside the zone.
915 * This allows to move further down until all regions are in the zone.
917 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
918 assert(ptr_y >= last_track_bottom_edge);
919 assert(_ddropzone > 0);
921 /* calculate mouse position in 'tracks' below last track. */
922 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
923 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
925 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
927 delta_time_axis_view = dzpos - _pdropzone;
928 } else if (dzpos < _pdropzone && _ndropzone > 0) {
929 // move up inside the DZ
930 delta_time_axis_view = dzpos - _pdropzone;
934 /* Work out the change in x */
935 framepos_t pending_region_position;
936 double const x_delta = compute_x_delta (event, &pending_region_position);
937 _last_frame_position = pending_region_position;
939 /* calculate hidden tracks in current y-axis delta */
941 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
942 /* The mouse is more than one track below the dropzone.
943 * distance calculation is not needed (and would not work, either
944 * because the dropzone is "packed").
946 * Except when [partially] moving regions out of dropzone in a large step.
947 * (the mouse may or may not remain in the DZ)
948 * Hidden tracks at the bottom of the TAV need to be skipped.
950 * This also handles the case if the mouse entered the DZ
951 * in a large step (exessive delta), either due to fast-movement,
952 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
954 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
955 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
957 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
958 -_time_axis_views.size() - dt;
961 else if (_last_pointer_time_axis_view < 0) {
962 /* Moving out of the zone. Check for hidden tracks at the bottom. */
963 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
964 -_time_axis_views.size() - delta_time_axis_view;
966 /* calculate hidden tracks that are skipped by the pointer movement */
967 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
968 - _last_pointer_time_axis_view
969 - delta_time_axis_view;
972 /* Verify change in y */
973 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
974 /* this y movement is not allowed, so do no y movement this time */
975 delta_time_axis_view = 0;
980 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
981 /* haven't reached next snap point, and we're not switching
982 trackviews nor layers. nothing to do.
987 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
988 PlaylistDropzoneMap playlist_dropzone_map;
989 _ndropzone = 0; // number of elements currently in the dropzone
992 /* sort views by time_axis.
993 * This retains track order in the dropzone, regardless
994 * of actual selection order
996 _views.sort (DraggingViewSorter());
998 /* count number of distinct tracks of all regions
999 * being dragged, used for dropzone.
1001 int prev_track = -1;
1002 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1003 if (i->time_axis_view != prev_track) {
1004 prev_track = i->time_axis_view;
1010 _views.back().time_axis_view -
1011 _views.front().time_axis_view;
1013 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1014 - _views.back().time_axis_view;
1016 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1020 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1022 RegionView* rv = i->view;
1027 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1034 /* reparent the regionview into a group above all
1038 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1039 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1040 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1041 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1042 /* move the item so that it continues to appear at the
1043 same location now that its parent has changed.
1045 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1048 /* If we have moved tracks, we'll fudge the layer delta so that the
1049 region gets moved back onto layer 0 on its new track; this avoids
1050 confusion when dragging regions from non-zero layers onto different
1053 double this_delta_layer = delta_layer;
1054 if (delta_time_axis_view != 0) {
1055 this_delta_layer = - i->layer;
1058 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1060 int track_index = i->time_axis_view + this_delta_time_axis_view;
1061 assert(track_index >= 0);
1063 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1064 /* Track is in the Dropzone */
1066 i->time_axis_view = track_index;
1067 assert(i->time_axis_view >= (int) _time_axis_views.size());
1070 double yposition = 0;
1071 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1072 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1075 /* store index of each new playlist as a negative count, starting at -1 */
1077 if (pdz == playlist_dropzone_map.end()) {
1078 /* compute where this new track (which doesn't exist yet) will live
1081 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1083 /* How high is this region view ? */
1085 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1086 ArdourCanvas::Rect bbox;
1089 bbox = obbox.get ();
1092 last_track_bottom_edge += bbox.height();
1094 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1097 yposition = pdz->second;
1100 /* values are zero or negative, hence the use of min() */
1101 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1106 /* The TimeAxisView that this region is now over */
1107 TimeAxisView* current_tv = _time_axis_views[track_index];
1109 /* Ensure it is moved from stacked -> expanded if appropriate */
1110 if (current_tv->view()->layer_display() == Stacked) {
1111 current_tv->view()->set_layer_display (Expanded);
1114 /* We're only allowed to go -ve in layer on Expanded views */
1115 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1116 this_delta_layer = - i->layer;
1120 rv->set_height (current_tv->view()->child_height ());
1122 /* Update show/hidden status as the region view may have come from a hidden track,
1123 or have moved to one.
1125 if (current_tv->hidden ()) {
1126 rv->get_canvas_group()->hide ();
1128 rv->get_canvas_group()->show ();
1131 /* Update the DraggingView */
1132 i->time_axis_view = track_index;
1133 i->layer += this_delta_layer;
1136 _editor->mouse_brush_insert_region (rv, pending_region_position);
1140 /* Get the y coordinate of the top of the track that this region is now over */
1141 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1143 /* And adjust for the layer that it should be on */
1144 StreamView* cv = current_tv->view ();
1145 switch (cv->layer_display ()) {
1149 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1152 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1156 /* need to get the parent of the regionview
1157 * canvas group and get its position in
1158 * equivalent coordinate space as the trackview
1159 * we are now dragging over.
1162 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1167 /* Now move the region view */
1168 rv->move (x_delta, y_delta);
1170 } /* foreach region */
1172 _total_x_delta += x_delta;
1174 if (x_delta != 0 && !_brushing) {
1175 show_verbose_cursor_time (_last_frame_position);
1178 /* keep track of pointer movement */
1180 /* the pointer is currently over a time axis view */
1182 if (_last_pointer_time_axis_view < 0) {
1183 /* last motion event was not over a time axis view
1184 * or last y-movement out of the dropzone was not valid
1187 if (delta_time_axis_view < 0) {
1188 /* in the drop zone, moving up */
1190 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1191 * We do not use negative _last_pointer_time_axis_view because
1192 * the dropzone is "packed" (the actual track offset is ignored)
1194 * As opposed to the actual number
1195 * of elements in the dropzone (_ndropzone)
1196 * _pdropzone is not constrained. This is necessary
1197 * to allow moving multiple regions with y-distance
1200 * There can be 0 elements in the dropzone,
1201 * even though the drag-pointer is inside the DZ.
1204 * [ Audio-track, Midi-track, Audio-track, DZ ]
1205 * move regions from both audio tracks at the same time into the
1206 * DZ by grabbing the region in the bottom track.
1208 assert(current_pointer_time_axis_view >= 0);
1209 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1213 /* only move out of the zone if the movement is OK */
1214 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1215 assert(delta_time_axis_view < 0);
1216 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1217 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1218 * the current position can be calculated as follows:
1220 // a well placed oofus attack can still throw this off.
1221 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1222 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1225 /* last motion event was also over a time axis view */
1226 _last_pointer_time_axis_view += delta_time_axis_view;
1227 assert(_last_pointer_time_axis_view >= 0);
1232 /* the pointer is not over a time axis view */
1233 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1234 _pdropzone += delta_time_axis_view - delta_skip;
1235 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1238 _last_pointer_layer += delta_layer;
1242 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1244 if (_copy && first_move) {
1245 if (_x_constrained && !_brushing) {
1246 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1247 } else if (!_brushing) {
1248 _editor->begin_reversible_command (Operations::region_copy);
1249 } else if (_brushing) {
1250 _editor->begin_reversible_command (Operations::drag_region_brush);
1252 /* duplicate the regionview(s) and region(s) */
1254 list<DraggingView> new_regionviews;
1256 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1258 RegionView* rv = i->view;
1259 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1260 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1262 const boost::shared_ptr<const Region> original = rv->region();
1263 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1264 region_copy->set_position (original->position());
1265 /* need to set this so that the drop zone code can work. This doesn't
1266 actually put the region into the playlist, but just sets a weak pointer
1269 region_copy->set_playlist (original->playlist());
1273 boost::shared_ptr<AudioRegion> audioregion_copy
1274 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1276 nrv = new AudioRegionView (*arv, audioregion_copy);
1278 boost::shared_ptr<MidiRegion> midiregion_copy
1279 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1280 nrv = new MidiRegionView (*mrv, midiregion_copy);
1285 nrv->get_canvas_group()->show ();
1286 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1288 /* swap _primary to the copy */
1290 if (rv == _primary) {
1294 /* ..and deselect the one we copied */
1296 rv->set_selected (false);
1299 if (!new_regionviews.empty()) {
1301 /* reflect the fact that we are dragging the copies */
1303 _views = new_regionviews;
1305 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1308 } else if (!_copy && first_move) {
1309 if (_x_constrained && !_brushing) {
1310 _editor->begin_reversible_command (_("fixed time region drag"));
1311 } else if (!_brushing) {
1312 _editor->begin_reversible_command (Operations::region_drag);
1313 } else if (_brushing) {
1314 _editor->begin_reversible_command (Operations::drag_region_brush);
1317 RegionMotionDrag::motion (event, first_move);
1321 RegionMotionDrag::finished (GdkEvent *, bool)
1323 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1324 if (!(*i)->view()) {
1328 if ((*i)->view()->layer_display() == Expanded) {
1329 (*i)->view()->set_layer_display (Stacked);
1335 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1337 RegionMotionDrag::finished (ev, movement_occurred);
1339 if (!movement_occurred) {
1343 if (was_double_click() && !_views.empty()) {
1344 DraggingView dv = _views.front();
1345 dv.view->show_region_editor ();
1352 assert (!_views.empty ());
1354 /* We might have hidden region views so that they weren't visible during the drag
1355 (when they have been reparented). Now everything can be shown again, as region
1356 views are back in their track parent groups.
1358 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1359 i->view->get_canvas_group()->show ();
1362 bool const changed_position = (_last_frame_position != _primary->region()->position());
1363 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1364 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1384 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1388 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1390 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1395 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1396 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1397 uint32_t output_chan = region->n_channels();
1398 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1399 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1401 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name());
1402 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1404 rtav->set_height (original->current_height());
1408 ChanCount one_midi_port (DataType::MIDI, 1);
1409 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1410 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1411 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1413 rtav->set_height (original->current_height());
1418 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1424 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1426 RegionSelection new_views;
1427 PlaylistSet modified_playlists;
1428 RouteTimeAxisView* new_time_axis_view = 0;
1431 /* all changes were made during motion event handlers */
1433 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1437 _editor->commit_reversible_command ();
1441 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1442 PlaylistMapping playlist_mapping;
1444 /* insert the regions into their new playlists */
1445 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1447 RouteTimeAxisView* dest_rtv = 0;
1449 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1455 if (changed_position && !_x_constrained) {
1456 where = i->view->region()->position() - drag_delta;
1458 where = i->view->region()->position();
1461 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1462 /* dragged to drop zone */
1464 PlaylistMapping::iterator pm;
1466 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1467 /* first region from this original playlist: create a new track */
1468 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1469 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1470 dest_rtv = new_time_axis_view;
1472 /* we already created a new track for regions from this playlist, use it */
1473 dest_rtv = pm->second;
1476 /* destination time axis view is the one we dragged to */
1477 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1480 if (dest_rtv != 0) {
1481 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1482 if (new_view != 0) {
1483 new_views.push_back (new_view);
1487 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1488 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1491 list<DraggingView>::const_iterator next = i;
1497 /* If we've created new regions either by copying or moving
1498 to a new track, we want to replace the old selection with the new ones
1501 if (new_views.size() > 0) {
1502 _editor->selection->set (new_views);
1505 /* write commands for the accumulated diffs for all our modified playlists */
1506 add_stateful_diff_commands_for_playlists (modified_playlists);
1508 _editor->commit_reversible_command ();
1512 RegionMoveDrag::finished_no_copy (
1513 bool const changed_position,
1514 bool const changed_tracks,
1515 framecnt_t const drag_delta
1518 RegionSelection new_views;
1519 PlaylistSet modified_playlists;
1520 PlaylistSet frozen_playlists;
1521 set<RouteTimeAxisView*> views_to_update;
1522 RouteTimeAxisView* new_time_axis_view = 0;
1524 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1525 PlaylistMapping playlist_mapping;
1527 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1529 RegionView* rv = i->view;
1530 RouteTimeAxisView* dest_rtv = 0;
1532 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1537 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1538 /* dragged to drop zone */
1540 PlaylistMapping::iterator pm;
1542 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1543 /* first region from this original playlist: create a new track */
1544 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1545 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1546 dest_rtv = new_time_axis_view;
1548 /* we already created a new track for regions from this playlist, use it */
1549 dest_rtv = pm->second;
1553 /* destination time axis view is the one we dragged to */
1554 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1559 double const dest_layer = i->layer;
1561 views_to_update.insert (dest_rtv);
1565 if (changed_position && !_x_constrained) {
1566 where = rv->region()->position() - drag_delta;
1568 where = rv->region()->position();
1571 if (changed_tracks) {
1573 /* insert into new playlist */
1575 RegionView* new_view = insert_region_into_playlist (
1576 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1579 if (new_view == 0) {
1584 new_views.push_back (new_view);
1586 /* remove from old playlist */
1588 /* the region that used to be in the old playlist is not
1589 moved to the new one - we use a copy of it. as a result,
1590 any existing editor for the region should no longer be
1593 rv->hide_region_editor();
1596 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1600 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1602 /* this movement may result in a crossfade being modified, or a layering change,
1603 so we need to get undo data from the playlist as well as the region.
1606 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1608 playlist->clear_changes ();
1611 rv->region()->clear_changes ();
1614 motion on the same track. plonk the previously reparented region
1615 back to its original canvas group (its streamview).
1616 No need to do anything for copies as they are fake regions which will be deleted.
1619 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1620 rv->get_canvas_group()->set_y_position (i->initial_y);
1623 /* just change the model */
1624 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1625 playlist->set_layer (rv->region(), dest_layer);
1628 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1630 r = frozen_playlists.insert (playlist);
1633 playlist->freeze ();
1636 rv->region()->set_position (where);
1637 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1640 if (changed_tracks) {
1642 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1643 was selected in all of them, then removing it from a playlist will have removed all
1644 trace of it from _views (i.e. there were N regions selected, we removed 1,
1645 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1646 corresponding regionview, and _views is now empty).
1648 This could have invalidated any and all iterators into _views.
1650 The heuristic we use here is: if the region selection is empty, break out of the loop
1651 here. if the region selection is not empty, then restart the loop because we know that
1652 we must have removed at least the region(view) we've just been working on as well as any
1653 that we processed on previous iterations.
1655 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1656 we can just iterate.
1660 if (_views.empty()) {
1671 /* If we've created new regions either by copying or moving
1672 to a new track, we want to replace the old selection with the new ones
1675 if (new_views.size() > 0) {
1676 _editor->selection->set (new_views);
1679 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1683 /* write commands for the accumulated diffs for all our modified playlists */
1684 add_stateful_diff_commands_for_playlists (modified_playlists);
1685 /* applies to _brushing */
1686 _editor->commit_reversible_command ();
1688 /* We have futzed with the layering of canvas items on our streamviews.
1689 If any region changed layer, this will have resulted in the stream
1690 views being asked to set up their region views, and all will be well.
1691 If not, we might now have badly-ordered region views. Ask the StreamViews
1692 involved to sort themselves out, just in case.
1695 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1696 (*i)->view()->playlist_layered ((*i)->track ());
1700 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1701 * @param region Region to remove.
1702 * @param playlist playlist To remove from.
1703 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1704 * that clear_changes () is only called once per playlist.
1707 RegionMoveDrag::remove_region_from_playlist (
1708 boost::shared_ptr<Region> region,
1709 boost::shared_ptr<Playlist> playlist,
1710 PlaylistSet& modified_playlists
1713 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1716 playlist->clear_changes ();
1719 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1723 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1724 * clearing the playlist's diff history first if necessary.
1725 * @param region Region to insert.
1726 * @param dest_rtv Destination RouteTimeAxisView.
1727 * @param dest_layer Destination layer.
1728 * @param where Destination position.
1729 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1730 * that clear_changes () is only called once per playlist.
1731 * @return New RegionView, or 0 if no insert was performed.
1734 RegionMoveDrag::insert_region_into_playlist (
1735 boost::shared_ptr<Region> region,
1736 RouteTimeAxisView* dest_rtv,
1739 PlaylistSet& modified_playlists
1742 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1743 if (!dest_playlist) {
1747 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1748 _new_region_view = 0;
1749 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1751 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1752 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1754 dest_playlist->clear_changes ();
1757 dest_playlist->add_region (region, where);
1759 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1760 dest_playlist->set_layer (region, dest_layer);
1765 assert (_new_region_view);
1767 return _new_region_view;
1771 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1773 _new_region_view = rv;
1777 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1779 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1780 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1782 _editor->session()->add_command (c);
1791 RegionMoveDrag::aborted (bool movement_occurred)
1795 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1796 list<DraggingView>::const_iterator next = i;
1805 RegionMotionDrag::aborted (movement_occurred);
1810 RegionMotionDrag::aborted (bool)
1812 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1814 StreamView* sview = (*i)->view();
1817 if (sview->layer_display() == Expanded) {
1818 sview->set_layer_display (Stacked);
1823 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1824 RegionView* rv = i->view;
1825 TimeAxisView* tv = &(rv->get_time_axis_view ());
1826 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1828 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1829 rv->get_canvas_group()->set_y_position (0);
1831 rv->move (-_total_x_delta, 0);
1832 rv->set_height (rtv->view()->child_height ());
1836 /** @param b true to brush, otherwise false.
1837 * @param c true to make copies of the regions being moved, otherwise false.
1839 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1840 : RegionMotionDrag (e, i, p, v, b)
1842 , _new_region_view (0)
1844 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1847 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1848 if (rtv && rtv->is_track()) {
1849 speed = rtv->track()->speed ();
1852 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1856 RegionMoveDrag::setup_pointer_frame_offset ()
1858 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1861 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1862 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1864 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1866 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1867 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1869 _primary = v->view()->create_region_view (r, false, false);
1871 _primary->get_canvas_group()->show ();
1872 _primary->set_position (pos, 0);
1873 _views.push_back (DraggingView (_primary, this, v));
1875 _last_frame_position = pos;
1877 _item = _primary->get_canvas_group ();
1881 RegionInsertDrag::finished (GdkEvent *, bool)
1883 int pos = _views.front().time_axis_view;
1884 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1886 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1888 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1889 _primary->get_canvas_group()->set_y_position (0);
1891 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1893 _editor->begin_reversible_command (Operations::insert_region);
1894 playlist->clear_changes ();
1895 playlist->add_region (_primary->region (), _last_frame_position);
1897 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1898 if (Config->get_edit_mode() == Ripple) {
1899 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1902 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1903 _editor->commit_reversible_command ();
1911 RegionInsertDrag::aborted (bool)
1918 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1919 : RegionMoveDrag (e, i, p, v, false, false)
1921 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1924 struct RegionSelectionByPosition {
1925 bool operator() (RegionView*a, RegionView* b) {
1926 return a->region()->position () < b->region()->position();
1931 RegionSpliceDrag::motion (GdkEvent* event, bool)
1933 /* Which trackview is this ? */
1935 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1936 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1938 /* The region motion is only processed if the pointer is over
1942 if (!tv || !tv->is_track()) {
1943 /* To make sure we hide the verbose canvas cursor when the mouse is
1944 not held over an audio track.
1946 _editor->verbose_cursor()->hide ();
1949 _editor->verbose_cursor()->show ();
1954 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1960 RegionSelection copy;
1961 _editor->selection->regions.by_position(copy);
1963 framepos_t const pf = adjusted_current_frame (event);
1965 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1967 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1973 boost::shared_ptr<Playlist> playlist;
1975 if ((playlist = atv->playlist()) == 0) {
1979 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1984 if (pf < (*i)->region()->last_frame() + 1) {
1988 if (pf > (*i)->region()->first_frame()) {
1994 playlist->shuffle ((*i)->region(), dir);
1999 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2001 RegionMoveDrag::finished (event, movement_occurred);
2005 RegionSpliceDrag::aborted (bool)
2015 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2018 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2020 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2021 RegionSelection to_ripple;
2022 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2023 if ((*i)->position() >= where) {
2024 to_ripple.push_back (rtv->view()->find_view(*i));
2028 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2029 if (!exclude.contains (*i)) {
2030 // the selection has already been added to _views
2032 if (drag_in_progress) {
2033 // do the same things that RegionMotionDrag::motion does when
2034 // first_move is true, for the region views that we're adding
2035 // to _views this time
2038 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2039 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2040 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2041 rvg->reparent (_editor->_drag_motion_group);
2043 // we only need to move in the y direction
2044 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2049 _views.push_back (DraggingView (*i, this, tav));
2055 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2058 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2059 // we added all the regions after the selection
2061 std::list<DraggingView>::iterator to_erase = i++;
2062 if (!_editor->selection->regions.contains (to_erase->view)) {
2063 // restore the non-selected regions to their original playlist & positions,
2064 // and then ripple them back by the length of the regions that were dragged away
2065 // do the same things as RegionMotionDrag::aborted
2067 RegionView *rv = to_erase->view;
2068 TimeAxisView* tv = &(rv->get_time_axis_view ());
2069 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2072 // plonk them back onto their own track
2073 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2074 rv->get_canvas_group()->set_y_position (0);
2078 // move the underlying region to match the view
2079 rv->region()->set_position (rv->region()->position() + amount);
2081 // restore the view to match the underlying region's original position
2082 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2085 rv->set_height (rtv->view()->child_height ());
2086 _views.erase (to_erase);
2092 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2094 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2096 return allow_moves_across_tracks;
2104 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2105 : RegionMoveDrag (e, i, p, v, false, false)
2107 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2108 // compute length of selection
2109 RegionSelection selected_regions = _editor->selection->regions;
2110 selection_length = selected_regions.end_frame() - selected_regions.start();
2112 // we'll only allow dragging to another track in ripple mode if all the regions
2113 // being dragged start off on the same track
2114 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2117 exclude = new RegionList;
2118 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2119 exclude->push_back((*i)->region());
2122 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2123 RegionSelection copy;
2124 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2126 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2127 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2129 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2130 // find ripple start point on each applicable playlist
2131 RegionView *first_selected_on_this_track = NULL;
2132 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2133 if ((*i)->region()->playlist() == (*pi)) {
2134 // region is on this playlist - it's the first, because they're sorted
2135 first_selected_on_this_track = *i;
2139 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2140 add_all_after_to_views (
2141 &first_selected_on_this_track->get_time_axis_view(),
2142 first_selected_on_this_track->region()->position(),
2143 selected_regions, false);
2146 if (allow_moves_across_tracks) {
2147 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2155 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2157 /* Which trackview is this ? */
2159 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2160 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2162 /* The region motion is only processed if the pointer is over
2166 if (!tv || !tv->is_track()) {
2167 /* To make sure we hide the verbose canvas cursor when the mouse is
2168 not held over an audiotrack.
2170 _editor->verbose_cursor()->hide ();
2174 framepos_t where = adjusted_current_frame (event);
2175 assert (where >= 0);
2177 double delta = compute_x_delta (event, &after);
2179 framecnt_t amount = _editor->pixel_to_sample (delta);
2181 if (allow_moves_across_tracks) {
2182 // all the originally selected regions were on the same track
2184 framecnt_t adjust = 0;
2185 if (prev_tav && tv != prev_tav) {
2186 // dragged onto a different track
2187 // remove the unselected regions from _views, restore them to their original positions
2188 // and add the regions after the drop point on the new playlist to _views instead.
2189 // undo the effect of rippling the previous playlist, and include the effect of removing
2190 // the dragged region(s) from this track
2192 remove_unselected_from_views (prev_amount, false);
2193 // ripple previous playlist according to the regions that have been removed onto the new playlist
2194 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2197 // move just the selected regions
2198 RegionMoveDrag::motion(event, first_move);
2200 // ensure that the ripple operation on the new playlist inserts selection_length time
2201 adjust = selection_length;
2202 // ripple the new current playlist
2203 tv->playlist()->ripple (where, amount+adjust, exclude);
2205 // add regions after point where drag entered this track to subsequent ripples
2206 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2209 // motion on same track
2210 RegionMoveDrag::motion(event, first_move);
2214 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2215 prev_position = where;
2217 // selection encompasses multiple tracks - just drag
2218 // cross-track drags are forbidden
2219 RegionMoveDrag::motion(event, first_move);
2222 if (!_x_constrained) {
2223 prev_amount += amount;
2226 _last_frame_position = after;
2230 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2232 if (!movement_occurred) {
2236 if (was_double_click() && !_views.empty()) {
2237 DraggingView dv = _views.front();
2238 dv.view->show_region_editor ();
2245 _editor->begin_reversible_command(_("Ripple drag"));
2247 // remove the regions being rippled from the dragging view, updating them to
2248 // their new positions
2249 remove_unselected_from_views (prev_amount, true);
2251 if (allow_moves_across_tracks) {
2253 // if regions were dragged across tracks, we've rippled any later
2254 // regions on the track the regions were dragged off, so we need
2255 // to add the original track to the undo record
2256 orig_tav->playlist()->clear_changes();
2257 vector<Command*> cmds;
2258 orig_tav->playlist()->rdiff (cmds);
2259 _editor->session()->add_commands (cmds);
2261 if (prev_tav && prev_tav != orig_tav) {
2262 prev_tav->playlist()->clear_changes();
2263 vector<Command*> cmds;
2264 prev_tav->playlist()->rdiff (cmds);
2265 _editor->session()->add_commands (cmds);
2268 // selection spanned multiple tracks - all will need adding to undo record
2270 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2271 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2273 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2274 (*pi)->clear_changes();
2275 vector<Command*> cmds;
2276 (*pi)->rdiff (cmds);
2277 _editor->session()->add_commands (cmds);
2281 // other modified playlists are added to undo by RegionMoveDrag::finished()
2282 RegionMoveDrag::finished (event, movement_occurred);
2283 _editor->commit_reversible_command();
2287 RegionRippleDrag::aborted (bool movement_occurred)
2289 RegionMoveDrag::aborted (movement_occurred);
2294 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2296 _view (dynamic_cast<MidiTimeAxisView*> (v))
2298 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2304 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2307 _editor->begin_reversible_command (_("create region"));
2308 _region = add_midi_region (_view, false);
2309 _view->playlist()->freeze ();
2312 framepos_t const f = adjusted_current_frame (event);
2313 if (f < grab_frame()) {
2314 _region->set_initial_position (f);
2317 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2318 so that if this region is duplicated, its duplicate starts on
2319 a snap point rather than 1 frame after a snap point. Otherwise things get
2320 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2321 place snapped notes at the start of the region.
2324 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2325 _region->set_length (len < 1 ? 1 : len);
2331 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2333 if (!movement_occurred) {
2334 add_midi_region (_view, true);
2336 _view->playlist()->thaw ();
2337 _editor->commit_reversible_command();
2342 RegionCreateDrag::aborted (bool)
2345 _view->playlist()->thaw ();
2351 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2356 , _was_selected (false)
2359 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2363 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2365 Gdk::Cursor* cursor;
2366 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2368 float x_fraction = cnote->mouse_x_fraction ();
2370 if (x_fraction > 0.0 && x_fraction < 0.25) {
2371 cursor = _editor->cursors()->left_side_trim;
2374 cursor = _editor->cursors()->right_side_trim;
2378 Drag::start_grab (event, cursor);
2380 region = &cnote->region_view();
2383 temp = region->snap_to_pixel (cnote->x0 (), true);
2384 _snap_delta = temp - cnote->x0 ();
2388 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2393 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2394 if (ms.size() > 1) {
2395 /* has to be relative, may make no sense otherwise */
2399 if (!(_was_selected = cnote->selected())) {
2401 /* tertiary-click means extend selection - we'll do that on button release,
2402 so don't add it here, because otherwise we make it hard to figure
2403 out the "extend-to" range.
2406 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2409 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2412 region->note_selected (cnote, true);
2414 _editor->get_selection().clear_points();
2415 region->unique_select (cnote);
2422 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2424 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2426 _editor->begin_reversible_command (_("resize notes"));
2428 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2429 MidiRegionSelection::iterator next;
2432 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2434 mrv->begin_resizing (at_front);
2440 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2441 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2443 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2447 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2449 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2450 if (_editor->snap_mode () != SnapOff) {
2454 if (_editor->snap_mode () == SnapOff) {
2456 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2457 if (apply_snap_delta) {
2463 if (apply_snap_delta) {
2467 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2473 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2475 if (!movement_occurred) {
2476 /* no motion - select note */
2477 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2478 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2479 _editor->current_mouse_mode() == Editing::MouseDraw) {
2481 bool changed = false;
2483 if (_was_selected) {
2484 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2486 region->note_deselected (cnote);
2489 _editor->get_selection().clear_points();
2490 region->unique_select (cnote);
2494 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2495 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2497 if (!extend && !add && region->selection_size() > 1) {
2498 _editor->get_selection().clear_points();
2499 region->unique_select (cnote);
2501 } else if (extend) {
2502 region->note_selected (cnote, true, true);
2505 /* it was added during button press */
2511 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2512 _editor->commit_reversible_selection_op();
2519 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2520 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2521 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2523 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2526 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2528 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2529 if (_editor->snap_mode () != SnapOff) {
2533 if (_editor->snap_mode () == SnapOff) {
2535 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2536 if (apply_snap_delta) {
2542 if (apply_snap_delta) {
2546 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2550 _editor->commit_reversible_command ();
2554 NoteResizeDrag::aborted (bool)
2556 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2557 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2558 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2560 mrv->abort_resizing ();
2565 AVDraggingView::AVDraggingView (RegionView* v)
2568 initial_position = v->region()->position ();
2571 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2574 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2577 TrackViewList empty;
2579 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2580 std::list<RegionView*> views = rs.by_layer();
2583 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2584 RegionView* rv = (*i);
2585 if (!rv->region()->video_locked()) {
2588 if (rv->region()->locked()) {
2591 _views.push_back (AVDraggingView (rv));
2596 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2598 Drag::start_grab (event);
2599 if (_editor->session() == 0) {
2603 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2609 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2613 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2614 _max_backwards_drag = (
2615 ARDOUR_UI::instance()->video_timeline->get_duration()
2616 + ARDOUR_UI::instance()->video_timeline->get_offset()
2617 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2620 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2621 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2622 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2625 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2628 Timecode::Time timecode;
2629 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2630 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);
2631 show_verbose_cursor_text (buf);
2635 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2637 if (_editor->session() == 0) {
2640 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2644 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2648 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2649 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2651 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2652 dt = - _max_backwards_drag;
2655 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2656 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2658 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2659 RegionView* rv = i->view;
2660 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2663 rv->region()->clear_changes ();
2664 rv->region()->suspend_property_changes();
2666 rv->region()->set_position(i->initial_position + dt);
2667 rv->region_changed(ARDOUR::Properties::position);
2670 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2671 Timecode::Time timecode;
2672 Timecode::Time timediff;
2674 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2675 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2676 snprintf (buf, sizeof (buf),
2677 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2678 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2679 , _("Video Start:"),
2680 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2682 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2684 show_verbose_cursor_text (buf);
2688 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2690 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2697 if (!movement_occurred || ! _editor->session()) {
2701 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2703 _editor->begin_reversible_command (_("Move Video"));
2705 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2706 ARDOUR_UI::instance()->video_timeline->save_undo();
2707 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2708 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2710 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2711 i->view->drag_end();
2712 i->view->region()->resume_property_changes ();
2714 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2717 _editor->session()->maybe_update_session_range(
2718 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2719 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2723 _editor->commit_reversible_command ();
2727 VideoTimeLineDrag::aborted (bool)
2729 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2732 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2733 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2735 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2736 i->view->region()->resume_property_changes ();
2737 i->view->region()->set_position(i->initial_position);
2741 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2742 : RegionDrag (e, i, p, v)
2743 , _operation (StartTrim)
2744 , _preserve_fade_anchor (preserve_fade_anchor)
2745 , _jump_position_when_done (false)
2747 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2751 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2754 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2755 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2757 if (tv && tv->is_track()) {
2758 speed = tv->track()->speed();
2761 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2762 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2763 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2765 framepos_t const pf = adjusted_current_frame (event);
2766 setup_snap_delta (region_start);
2768 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2769 /* Move the contents of the region around without changing the region bounds */
2770 _operation = ContentsTrim;
2771 Drag::start_grab (event, _editor->cursors()->trimmer);
2773 /* These will get overridden for a point trim.*/
2774 if (pf < (region_start + region_length/2)) {
2775 /* closer to front */
2776 _operation = StartTrim;
2777 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2778 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2780 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2784 _operation = EndTrim;
2785 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2786 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2788 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2792 /* jump trim disabled for now
2793 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2794 _jump_position_when_done = true;
2798 switch (_operation) {
2800 show_verbose_cursor_time (region_start);
2803 show_verbose_cursor_duration (region_start, region_end);
2806 show_verbose_cursor_time (pf);
2810 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2811 i->view->region()->suspend_property_changes ();
2816 TrimDrag::motion (GdkEvent* event, bool first_move)
2818 RegionView* rv = _primary;
2821 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2822 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2823 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2824 frameoffset_t frame_delta = 0;
2826 if (tv && tv->is_track()) {
2827 speed = tv->track()->speed();
2829 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2830 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2836 switch (_operation) {
2838 trim_type = "Region start trim";
2841 trim_type = "Region end trim";
2844 trim_type = "Region content trim";
2851 _editor->begin_reversible_command (trim_type);
2853 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2854 RegionView* rv = i->view;
2855 rv->enable_display (false);
2856 rv->region()->playlist()->clear_owned_changes ();
2858 if (_operation == StartTrim) {
2859 rv->trim_front_starting ();
2862 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2865 arv->temporarily_hide_envelope ();
2869 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2870 insert_result = _editor->motion_frozen_playlists.insert (pl);
2872 if (insert_result.second) {
2878 bool non_overlap_trim = false;
2880 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2881 non_overlap_trim = true;
2884 /* contstrain trim to fade length */
2885 if (_preserve_fade_anchor) {
2886 switch (_operation) {
2888 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2889 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2891 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2892 if (ar->locked()) continue;
2893 framecnt_t len = ar->fade_in()->back()->when;
2894 if (len < dt) dt = min(dt, len);
2898 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2899 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2901 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2902 if (ar->locked()) continue;
2903 framecnt_t len = ar->fade_out()->back()->when;
2904 if (len < -dt) dt = max(dt, -len);
2913 switch (_operation) {
2915 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2916 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2917 if (changed && _preserve_fade_anchor) {
2918 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2920 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2921 framecnt_t len = ar->fade_in()->back()->when;
2922 framecnt_t diff = ar->first_frame() - i->initial_position;
2923 framepos_t new_length = len - diff;
2924 i->anchored_fade_length = min (ar->length(), new_length);
2925 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2926 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2933 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2934 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2935 if (changed && _preserve_fade_anchor) {
2936 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2938 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2939 framecnt_t len = ar->fade_out()->back()->when;
2940 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2941 framepos_t new_length = len + diff;
2942 i->anchored_fade_length = min (ar->length(), new_length);
2943 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2944 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2952 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2954 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2955 i->view->move_contents (frame_delta);
2961 switch (_operation) {
2963 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2966 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2969 // show_verbose_cursor_time (frame_delta);
2975 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2977 if (movement_occurred) {
2978 motion (event, false);
2980 if (_operation == StartTrim) {
2981 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2983 /* This must happen before the region's StatefulDiffCommand is created, as it may
2984 `correct' (ahem) the region's _start from being negative to being zero. It
2985 needs to be zero in the undo record.
2987 i->view->trim_front_ending ();
2989 if (_preserve_fade_anchor) {
2990 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2992 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2993 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2994 ar->set_fade_in_length(i->anchored_fade_length);
2995 ar->set_fade_in_active(true);
2998 if (_jump_position_when_done) {
2999 i->view->region()->set_position (i->initial_position);
3002 } else if (_operation == EndTrim) {
3003 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3004 if (_preserve_fade_anchor) {
3005 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3007 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3008 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3009 ar->set_fade_out_length(i->anchored_fade_length);
3010 ar->set_fade_out_active(true);
3013 if (_jump_position_when_done) {
3014 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3019 if (!_views.empty()) {
3020 if (_operation == StartTrim) {
3021 _editor->maybe_locate_with_edit_preroll(
3022 _views.begin()->view->region()->position());
3024 if (_operation == EndTrim) {
3025 _editor->maybe_locate_with_edit_preroll(
3026 _views.begin()->view->region()->position() +
3027 _views.begin()->view->region()->length());
3031 if (!_editor->selection->selected (_primary)) {
3032 _primary->thaw_after_trim ();
3035 set<boost::shared_ptr<Playlist> > diffed_playlists;
3037 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3038 i->view->thaw_after_trim ();
3039 i->view->enable_display (true);
3041 /* Trimming one region may affect others on the playlist, so we need
3042 to get undo Commands from the whole playlist rather than just the
3043 region. Use diffed_playlists to make sure we don't diff a given
3044 playlist more than once.
3046 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3047 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3048 vector<Command*> cmds;
3050 _editor->session()->add_commands (cmds);
3051 diffed_playlists.insert (p);
3056 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3060 _editor->motion_frozen_playlists.clear ();
3061 _editor->commit_reversible_command();
3064 /* no mouse movement */
3065 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3066 _editor->point_trim (event, adjusted_current_frame (event));
3070 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3071 i->view->region()->resume_property_changes ();
3076 TrimDrag::aborted (bool movement_occurred)
3078 /* Our motion method is changing model state, so use the Undo system
3079 to cancel. Perhaps not ideal, as this will leave an Undo point
3080 behind which may be slightly odd from the user's point of view.
3085 if (movement_occurred) {
3089 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3090 i->view->region()->resume_property_changes ();
3095 TrimDrag::setup_pointer_frame_offset ()
3097 list<DraggingView>::iterator i = _views.begin ();
3098 while (i != _views.end() && i->view != _primary) {
3102 if (i == _views.end()) {
3106 switch (_operation) {
3108 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3111 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3118 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3123 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3124 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3129 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3131 Drag::start_grab (event, cursor);
3132 show_verbose_cursor_time (adjusted_current_frame(event));
3136 MeterMarkerDrag::setup_pointer_frame_offset ()
3138 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3142 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3144 if (!_marker->meter().movable()) {
3150 // create a dummy marker for visual representation of moving the
3151 // section, because whether its a copy or not, we're going to
3152 // leave or lose the original marker (leave if its a copy; lose if its
3153 // not, because we'll remove it from the map).
3155 MeterSection section (_marker->meter());
3157 if (!section.movable()) {
3162 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3164 _marker = new MeterMarker (
3166 *_editor->meter_group,
3167 UIConfiguration::instance().color ("meter marker"),
3169 *new MeterSection (_marker->meter())
3172 /* use the new marker for the grab */
3173 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3176 TempoMap& map (_editor->session()->tempo_map());
3177 /* get current state */
3178 before_state = &map.get_state();
3179 /* remove the section while we drag it */
3180 map.remove_meter (section, true);
3184 framepos_t const pf = adjusted_current_frame (event);
3186 _marker->set_position (pf);
3187 show_verbose_cursor_time (pf);
3191 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3193 if (!movement_occurred) {
3194 if (was_double_click()) {
3195 _editor->edit_meter_marker (*_marker);
3200 if (!_marker->meter().movable()) {
3204 motion (event, false);
3206 Timecode::BBT_Time when;
3208 TempoMap& map (_editor->session()->tempo_map());
3209 map.bbt_time (_marker->position(), when);
3211 if (_copy == true) {
3212 _editor->begin_reversible_command (_("copy meter mark"));
3213 XMLNode &before = map.get_state();
3214 map.add_meter (_marker->meter(), when);
3215 XMLNode &after = map.get_state();
3216 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3217 _editor->commit_reversible_command ();
3220 _editor->begin_reversible_command (_("move meter mark"));
3222 /* we removed it before, so add it back now */
3224 map.add_meter (_marker->meter(), when);
3225 XMLNode &after = map.get_state();
3226 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3227 _editor->commit_reversible_command ();
3230 // delete the dummy marker we used for visual representation while moving.
3231 // a new visual marker will show up automatically.
3236 MeterMarkerDrag::aborted (bool moved)
3238 _marker->set_position (_marker->meter().frame ());
3241 TempoMap& map (_editor->session()->tempo_map());
3242 /* we removed it before, so add it back now */
3243 map.add_meter (_marker->meter(), _marker->meter().frame());
3244 // delete the dummy marker we used for visual representation while moving.
3245 // a new visual marker will show up automatically.
3250 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3255 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3257 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3262 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3264 Drag::start_grab (event, cursor);
3265 show_verbose_cursor_time (adjusted_current_frame (event));
3269 TempoMarkerDrag::setup_pointer_frame_offset ()
3271 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3275 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3277 if (!_marker->tempo().movable()) {
3283 // create a dummy marker for visual representation of moving the
3284 // section, because whether its a copy or not, we're going to
3285 // leave or lose the original marker (leave if its a copy; lose if its
3286 // not, because we'll remove it from the map).
3288 // create a dummy marker for visual representation of moving the copy.
3289 // The actual copying is not done before we reach the finish callback.
3292 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3294 TempoSection section (_marker->tempo());
3296 _marker = new TempoMarker (
3298 *_editor->tempo_group,
3299 UIConfiguration::instance().color ("tempo marker"),
3301 *new TempoSection (_marker->tempo())
3304 /* use the new marker for the grab */
3305 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3308 _editor->begin_reversible_command (_("move tempo mark"));
3309 TempoMap& map (_editor->session()->tempo_map());
3310 /* get current state */
3311 before_state = &map.get_state();
3312 /* remove the section while we drag it */
3313 map.remove_tempo (section, true);
3317 framepos_t const pf = adjusted_current_frame (event);
3318 _marker->set_position (pf);
3319 show_verbose_cursor_time (pf);
3323 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3325 if (!movement_occurred) {
3326 if (was_double_click()) {
3327 _editor->edit_tempo_marker (*_marker);
3332 if (!_marker->tempo().movable()) {
3336 motion (event, false);
3338 TempoMap& map (_editor->session()->tempo_map());
3339 framepos_t beat_time = map.round_to_beat (_marker->position(), RoundNearest);
3340 Timecode::BBT_Time when;
3342 map.bbt_time (beat_time, when);
3344 if (_copy == true) {
3345 _editor->begin_reversible_command (_("copy tempo mark"));
3346 XMLNode &before = map.get_state();
3347 map.add_tempo (_marker->tempo(), when);
3348 XMLNode &after = map.get_state();
3349 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3350 _editor->commit_reversible_command ();
3353 /* we removed it before, so add it back now */
3354 map.add_tempo (_marker->tempo(), when);
3355 XMLNode &after = map.get_state();
3356 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3357 _editor->commit_reversible_command ();
3360 // delete the dummy marker we used for visual representation while moving.
3361 // a new visual marker will show up automatically.
3366 TempoMarkerDrag::aborted (bool moved)
3368 _marker->set_position (_marker->tempo().frame());
3370 TempoMap& map (_editor->session()->tempo_map());
3371 /* we removed it before, so add it back now */
3372 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3373 // delete the dummy marker we used for visual representation while moving.
3374 // a new visual marker will show up automatically.
3379 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3380 : Drag (e, &c.track_canvas_item(), false)
3385 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3388 /** Do all the things we do when dragging the playhead to make it look as though
3389 * we have located, without actually doing the locate (because that would cause
3390 * the diskstream buffers to be refilled, which is too slow).
3393 CursorDrag::fake_locate (framepos_t t)
3395 if (_editor->session () == 0) {
3399 _editor->playhead_cursor->set_position (t);
3401 Session* s = _editor->session ();
3402 if (s->timecode_transmission_suspended ()) {
3403 framepos_t const f = _editor->playhead_cursor->current_frame ();
3404 /* This is asynchronous so it will be sent "now"
3406 s->send_mmc_locate (f);
3407 /* These are synchronous and will be sent during the next
3410 s->queue_full_time_code ();
3411 s->queue_song_position_pointer ();
3414 show_verbose_cursor_time (t);
3415 _editor->UpdateAllTransportClocks (t);
3419 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3421 Drag::start_grab (event, c);
3422 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3424 _grab_zoom = _editor->samples_per_pixel;
3426 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3428 _editor->snap_to_with_modifier (where, event);
3430 _editor->_dragging_playhead = true;
3432 Session* s = _editor->session ();
3434 /* grab the track canvas item as well */
3436 _cursor.track_canvas_item().grab();
3439 if (_was_rolling && _stop) {
3443 if (s->is_auditioning()) {
3444 s->cancel_audition ();
3448 if (AudioEngine::instance()->connected()) {
3450 /* do this only if we're the engine is connected
3451 * because otherwise this request will never be
3452 * serviced and we'll busy wait forever. likewise,
3453 * notice if we are disconnected while waiting for the
3454 * request to be serviced.
3457 s->request_suspend_timecode_transmission ();
3458 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3459 /* twiddle our thumbs */
3464 fake_locate (where - snap_delta (event->button.state));
3468 CursorDrag::motion (GdkEvent* event, bool)
3470 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3471 _editor->snap_to_with_modifier (where, event);
3472 if (where != last_pointer_frame()) {
3473 fake_locate (where - snap_delta (event->button.state));
3478 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3480 _editor->_dragging_playhead = false;
3482 _cursor.track_canvas_item().ungrab();
3484 if (!movement_occurred && _stop) {
3488 motion (event, false);
3490 Session* s = _editor->session ();
3492 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3493 _editor->_pending_locate_request = true;
3494 s->request_resume_timecode_transmission ();
3499 CursorDrag::aborted (bool)
3501 _cursor.track_canvas_item().ungrab();
3503 if (_editor->_dragging_playhead) {
3504 _editor->session()->request_resume_timecode_transmission ();
3505 _editor->_dragging_playhead = false;
3508 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3511 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3512 : RegionDrag (e, i, p, v)
3514 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3518 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3520 Drag::start_grab (event, cursor);
3522 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3523 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3524 setup_snap_delta (r->position ());
3526 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3530 FadeInDrag::setup_pointer_frame_offset ()
3532 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3533 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3534 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3538 FadeInDrag::motion (GdkEvent* event, bool)
3540 framecnt_t fade_length;
3542 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3543 _editor->snap_to_with_modifier (pos, event);
3544 pos -= snap_delta (event->button.state);
3546 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3548 if (pos < (region->position() + 64)) {
3549 fade_length = 64; // this should be a minimum defined somewhere
3550 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3551 fade_length = region->length() - region->fade_out()->back()->when - 1;
3553 fade_length = pos - region->position();
3556 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3558 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3564 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3567 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3571 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3573 if (!movement_occurred) {
3577 framecnt_t fade_length;
3578 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3579 _editor->snap_to_with_modifier (pos, event);
3580 pos -= snap_delta (event->button.state);
3582 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3584 if (pos < (region->position() + 64)) {
3585 fade_length = 64; // this should be a minimum defined somewhere
3586 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3587 fade_length = region->length() - region->fade_out()->back()->when - 1;
3589 fade_length = pos - region->position();
3592 bool in_command = false;
3594 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3596 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3602 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3603 XMLNode &before = alist->get_state();
3605 tmp->audio_region()->set_fade_in_length (fade_length);
3606 tmp->audio_region()->set_fade_in_active (true);
3609 _editor->begin_reversible_command (_("change fade in length"));
3612 XMLNode &after = alist->get_state();
3613 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3617 _editor->commit_reversible_command ();
3622 FadeInDrag::aborted (bool)
3624 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3625 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3631 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3635 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3636 : RegionDrag (e, i, p, v)
3638 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3642 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3644 Drag::start_grab (event, cursor);
3646 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3647 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3648 setup_snap_delta (r->last_frame ());
3650 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3654 FadeOutDrag::setup_pointer_frame_offset ()
3656 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3657 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3658 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3662 FadeOutDrag::motion (GdkEvent* event, bool)
3664 framecnt_t fade_length;
3666 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3667 _editor->snap_to_with_modifier (pos, event);
3668 pos -= snap_delta (event->button.state);
3670 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3672 if (pos > (region->last_frame() - 64)) {
3673 fade_length = 64; // this should really be a minimum fade defined somewhere
3674 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3675 fade_length = region->length() - region->fade_in()->back()->when - 1;
3677 fade_length = region->last_frame() - pos;
3680 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3682 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3688 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3691 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3695 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3697 if (!movement_occurred) {
3701 framecnt_t fade_length;
3703 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3704 _editor->snap_to_with_modifier (pos, event);
3705 pos -= snap_delta (event->button.state);
3707 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3709 if (pos > (region->last_frame() - 64)) {
3710 fade_length = 64; // this should really be a minimum fade defined somewhere
3711 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3712 fade_length = region->length() - region->fade_in()->back()->when - 1;
3714 fade_length = region->last_frame() - pos;
3717 bool in_command = false;
3719 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3721 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3727 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3728 XMLNode &before = alist->get_state();
3730 tmp->audio_region()->set_fade_out_length (fade_length);
3731 tmp->audio_region()->set_fade_out_active (true);
3734 _editor->begin_reversible_command (_("change fade out length"));
3737 XMLNode &after = alist->get_state();
3738 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3742 _editor->commit_reversible_command ();
3747 FadeOutDrag::aborted (bool)
3749 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3750 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3756 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3760 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3762 , _selection_changed (false)
3764 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3766 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3769 _points.push_back (ArdourCanvas::Duple (0, 0));
3770 _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3773 MarkerDrag::~MarkerDrag ()
3775 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3780 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3782 location = new Location (*l);
3783 markers.push_back (m);
3788 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3790 Drag::start_grab (event, cursor);
3794 Location *location = _editor->find_location_from_marker (_marker, is_start);
3795 _editor->_dragging_edit_point = true;
3797 update_item (location);
3799 // _drag_line->show();
3800 // _line->raise_to_top();
3803 show_verbose_cursor_time (location->start());
3805 show_verbose_cursor_time (location->end());
3807 setup_snap_delta (is_start ? location->start() : location->end());
3809 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3812 case Selection::Toggle:
3813 /* we toggle on the button release */
3815 case Selection::Set:
3816 if (!_editor->selection->selected (_marker)) {
3817 _editor->selection->set (_marker);
3818 _selection_changed = true;
3821 case Selection::Extend:
3823 Locations::LocationList ll;
3824 list<ArdourMarker*> to_add;
3826 _editor->selection->markers.range (s, e);
3827 s = min (_marker->position(), s);
3828 e = max (_marker->position(), e);
3831 if (e < max_framepos) {
3834 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3835 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3836 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3839 to_add.push_back (lm->start);
3842 to_add.push_back (lm->end);
3846 if (!to_add.empty()) {
3847 _editor->selection->add (to_add);
3848 _selection_changed = true;
3852 case Selection::Add:
3853 _editor->selection->add (_marker);
3854 _selection_changed = true;
3859 /* Set up copies for us to manipulate during the drag
3862 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3864 Location* l = _editor->find_location_from_marker (*i, is_start);
3871 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3873 /* range: check that the other end of the range isn't
3876 CopiedLocationInfo::iterator x;
3877 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3878 if (*(*x).location == *l) {
3882 if (x == _copied_locations.end()) {
3883 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3885 (*x).markers.push_back (*i);
3886 (*x).move_both = true;
3894 MarkerDrag::setup_pointer_frame_offset ()
3897 Location *location = _editor->find_location_from_marker (_marker, is_start);
3898 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3902 MarkerDrag::motion (GdkEvent* event, bool)
3904 framecnt_t f_delta = 0;
3906 bool move_both = false;
3907 Location *real_location;
3908 Location *copy_location = 0;
3909 framecnt_t const sd = snap_delta (event->button.state);
3911 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
3912 framepos_t next = newframe;
3914 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
3918 CopiedLocationInfo::iterator x;
3920 /* find the marker we're dragging, and compute the delta */
3922 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3924 copy_location = (*x).location;
3926 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3928 /* this marker is represented by this
3929 * CopiedLocationMarkerInfo
3932 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3937 if (real_location->is_mark()) {
3938 f_delta = newframe - copy_location->start();
3942 switch (_marker->type()) {
3943 case ArdourMarker::SessionStart:
3944 case ArdourMarker::RangeStart:
3945 case ArdourMarker::LoopStart:
3946 case ArdourMarker::PunchIn:
3947 f_delta = newframe - copy_location->start();
3950 case ArdourMarker::SessionEnd:
3951 case ArdourMarker::RangeEnd:
3952 case ArdourMarker::LoopEnd:
3953 case ArdourMarker::PunchOut:
3954 f_delta = newframe - copy_location->end();
3957 /* what kind of marker is this ? */
3966 if (x == _copied_locations.end()) {
3967 /* hmm, impossible - we didn't find the dragged marker */
3971 /* now move them all */
3973 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3975 copy_location = x->location;
3977 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3981 if (real_location->locked()) {
3985 if (copy_location->is_mark()) {
3989 copy_location->set_start (copy_location->start() + f_delta);
3993 framepos_t new_start = copy_location->start() + f_delta;
3994 framepos_t new_end = copy_location->end() + f_delta;
3996 if (is_start) { // start-of-range marker
3998 if (move_both || (*x).move_both) {
3999 copy_location->set_start (new_start);
4000 copy_location->set_end (new_end);
4001 } else if (new_start < copy_location->end()) {
4002 copy_location->set_start (new_start);
4003 } else if (newframe > 0) {
4004 //_editor->snap_to (next, RoundUpAlways, true);
4005 copy_location->set_end (next);
4006 copy_location->set_start (newframe);
4009 } else { // end marker
4011 if (move_both || (*x).move_both) {
4012 copy_location->set_end (new_end);
4013 copy_location->set_start (new_start);
4014 } else if (new_end > copy_location->start()) {
4015 copy_location->set_end (new_end);
4016 } else if (newframe > 0) {
4017 //_editor->snap_to (next, RoundDownAlways, true);
4018 copy_location->set_start (next);
4019 copy_location->set_end (newframe);
4024 update_item (copy_location);
4026 /* now lookup the actual GUI items used to display this
4027 * location and move them to wherever the copy of the location
4028 * is now. This means that the logic in ARDOUR::Location is
4029 * still enforced, even though we are not (yet) modifying
4030 * the real Location itself.
4033 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4036 lm->set_position (copy_location->start(), copy_location->end());
4041 assert (!_copied_locations.empty());
4043 show_verbose_cursor_time (newframe);
4047 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4049 if (!movement_occurred) {
4051 if (was_double_click()) {
4052 _editor->rename_marker (_marker);
4056 /* just a click, do nothing but finish
4057 off the selection process
4060 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4062 case Selection::Set:
4063 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4064 _editor->selection->set (_marker);
4065 _selection_changed = true;
4069 case Selection::Toggle:
4070 /* we toggle on the button release, click only */
4071 _editor->selection->toggle (_marker);
4072 _selection_changed = true;
4076 case Selection::Extend:
4077 case Selection::Add:
4081 if (_selection_changed) {
4082 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4083 _editor->commit_reversible_selection_op();
4089 _editor->_dragging_edit_point = false;
4091 XMLNode &before = _editor->session()->locations()->get_state();
4092 bool in_command = false;
4094 MarkerSelection::iterator i;
4095 CopiedLocationInfo::iterator x;
4098 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4099 x != _copied_locations.end() && i != _editor->selection->markers.end();
4102 Location * location = _editor->find_location_from_marker (*i, is_start);
4106 if (location->locked()) {
4110 _editor->begin_reversible_command ( _("move marker") );
4113 if (location->is_mark()) {
4114 location->set_start (((*x).location)->start());
4116 location->set (((*x).location)->start(), ((*x).location)->end());
4122 XMLNode &after = _editor->session()->locations()->get_state();
4123 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4124 _editor->commit_reversible_command ();
4129 MarkerDrag::aborted (bool movement_occured)
4131 if (!movement_occured) {
4135 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4137 /* move all markers to their original location */
4140 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4143 Location * location = _editor->find_location_from_marker (*m, is_start);
4146 (*m)->set_position (is_start ? location->start() : location->end());
4153 MarkerDrag::update_item (Location*)
4158 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4160 , _fixed_grab_x (0.0)
4161 , _fixed_grab_y (0.0)
4162 , _cumulative_x_drag (0.0)
4163 , _cumulative_y_drag (0.0)
4167 if (_zero_gain_fraction < 0.0) {
4168 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4171 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4173 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4179 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4181 Drag::start_grab (event, _editor->cursors()->fader);
4183 // start the grab at the center of the control point so
4184 // the point doesn't 'jump' to the mouse after the first drag
4185 _fixed_grab_x = _point->get_x();
4186 _fixed_grab_y = _point->get_y();
4188 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4189 setup_snap_delta (pos);
4191 float const fraction = 1 - (_point->get_y() / _point->line().height());
4192 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4194 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4196 if (!_point->can_slide ()) {
4197 _x_constrained = true;
4202 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4204 double dx = _drags->current_pointer_x() - last_pointer_x();
4205 double dy = current_pointer_y() - last_pointer_y();
4206 bool need_snap = true;
4208 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4214 /* coordinate in pixels relative to the start of the region (for region-based automation)
4215 or track (for track-based automation) */
4216 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4217 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4219 // calculate zero crossing point. back off by .01 to stay on the
4220 // positive side of zero
4221 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4223 if (_x_constrained) {
4226 if (_y_constrained) {
4230 _cumulative_x_drag = cx - _fixed_grab_x;
4231 _cumulative_y_drag = cy - _fixed_grab_y;
4235 cy = min ((double) _point->line().height(), cy);
4237 // make sure we hit zero when passing through
4238 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4242 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4244 if (!_x_constrained && need_snap) {
4245 _editor->snap_to_with_modifier (cx_frames, event);
4248 cx_frames -= snap_delta (event->button.state);
4249 cx_frames = min (cx_frames, _point->line().maximum_time());
4251 float const fraction = 1.0 - (cy / _point->line().height());
4254 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4255 _editor->begin_reversible_command (_("automation event move"));
4256 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4258 pair<double, float> result;
4259 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4261 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4265 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4267 if (!movement_occurred) {
4270 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4271 _editor->reset_point_selection ();
4275 _point->line().end_drag (_pushing, _final_index);
4276 _editor->commit_reversible_command ();
4281 ControlPointDrag::aborted (bool)
4283 _point->line().reset ();
4287 ControlPointDrag::active (Editing::MouseMode m)
4289 if (m == Editing::MouseDraw) {
4290 /* always active in mouse draw */
4294 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4295 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4298 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4301 , _fixed_grab_x (0.0)
4302 , _fixed_grab_y (0.0)
4303 , _cumulative_y_drag (0)
4307 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4311 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4313 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4316 _item = &_line->grab_item ();
4318 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4319 origin, and ditto for y.
4322 double mx = event->button.x;
4323 double my = event->button.y;
4325 _line->grab_item().canvas_to_item (mx, my);
4327 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4329 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4330 /* no adjacent points */
4334 Drag::start_grab (event, _editor->cursors()->fader);
4336 /* store grab start in item frame */
4337 double const bx = _line->nth (_before)->get_x();
4338 double const ax = _line->nth (_after)->get_x();
4339 double const click_ratio = (ax - mx) / (ax - bx);
4341 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4346 double fraction = 1.0 - (cy / _line->height());
4348 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4352 LineDrag::motion (GdkEvent* event, bool first_move)
4354 double dy = current_pointer_y() - last_pointer_y();
4356 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4360 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4362 _cumulative_y_drag = cy - _fixed_grab_y;
4365 cy = min ((double) _line->height(), cy);
4367 double const fraction = 1.0 - (cy / _line->height());
4371 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4373 _editor->begin_reversible_command (_("automation range move"));
4374 _line->start_drag_line (_before, _after, initial_fraction);
4377 /* we are ignoring x position for this drag, so we can just pass in anything */
4378 pair<double, float> result;
4380 result = _line->drag_motion (0, fraction, true, false, ignored);
4381 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4385 LineDrag::finished (GdkEvent* event, bool movement_occured)
4387 if (movement_occured) {
4388 motion (event, false);
4389 _line->end_drag (false, 0);
4390 _editor->commit_reversible_command ();
4392 /* add a new control point on the line */
4394 AutomationTimeAxisView* atv;
4396 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4397 framepos_t where = grab_frame ();
4400 double cy = _fixed_grab_y;
4402 _line->grab_item().item_to_canvas (cx, cy);
4404 atv->add_automation_event (event, where, cy, false);
4405 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4406 AudioRegionView* arv;
4408 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4409 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4416 LineDrag::aborted (bool)
4421 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4425 _region_view_grab_x (0.0),
4426 _cumulative_x_drag (0),
4430 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4434 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4436 Drag::start_grab (event);
4438 _line = reinterpret_cast<Line*> (_item);
4441 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4443 double cx = event->button.x;
4444 double cy = event->button.y;
4446 _item->parent()->canvas_to_item (cx, cy);
4448 /* store grab start in parent frame */
4449 _region_view_grab_x = cx;
4451 _before = *(float*) _item->get_data ("position");
4453 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4455 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4459 FeatureLineDrag::motion (GdkEvent*, bool)
4461 double dx = _drags->current_pointer_x() - last_pointer_x();
4463 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4465 _cumulative_x_drag += dx;
4467 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4476 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4478 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4480 float *pos = new float;
4483 _line->set_data ("position", pos);
4489 FeatureLineDrag::finished (GdkEvent*, bool)
4491 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4492 _arv->update_transient(_before, _before);
4496 FeatureLineDrag::aborted (bool)
4501 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4503 , _vertical_only (false)
4505 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4509 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4511 Drag::start_grab (event);
4512 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4516 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4523 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4525 framepos_t grab = grab_frame ();
4526 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4527 _editor->snap_to_with_modifier (grab, event);
4529 grab = raw_grab_frame ();
4532 /* base start and end on initial click position */
4542 if (current_pointer_y() < grab_y()) {
4543 y1 = current_pointer_y();
4546 y2 = current_pointer_y();
4550 if (start != end || y1 != y2) {
4552 double x1 = _editor->sample_to_pixel (start);
4553 double x2 = _editor->sample_to_pixel (end);
4554 const double min_dimension = 2.0;
4556 if (_vertical_only) {
4557 /* fixed 10 pixel width */
4561 x2 = min (x1 - min_dimension, x2);
4563 x2 = max (x1 + min_dimension, x2);
4568 y2 = min (y1 - min_dimension, y2);
4570 y2 = max (y1 + min_dimension, y2);
4573 /* translate rect into item space and set */
4575 ArdourCanvas::Rect r (x1, y1, x2, y2);
4577 /* this drag is a _trackview_only == true drag, so the y1 and
4578 * y2 (computed using current_pointer_y() and grab_y()) will be
4579 * relative to the top of the trackview group). The
4580 * rubberband rect has the same parent/scroll offset as the
4581 * the trackview group, so we can use the "r" rect directly
4582 * to set the shape of the rubberband.
4585 _editor->rubberband_rect->set (r);
4586 _editor->rubberband_rect->show();
4587 _editor->rubberband_rect->raise_to_top();
4589 show_verbose_cursor_time (pf);
4591 do_select_things (event, true);
4596 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4600 framepos_t grab = grab_frame ();
4601 framepos_t lpf = last_pointer_frame ();
4603 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4604 grab = raw_grab_frame ();
4605 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4619 if (current_pointer_y() < grab_y()) {
4620 y1 = current_pointer_y();
4623 y2 = current_pointer_y();
4627 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4631 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4633 if (movement_occurred) {
4635 motion (event, false);
4636 do_select_things (event, false);
4642 bool do_deselect = true;
4643 MidiTimeAxisView* mtv;
4645 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4647 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4648 /* nothing selected */
4649 add_midi_region (mtv, true);
4650 do_deselect = false;
4654 /* do not deselect if Primary or Tertiary (toggle-select or
4655 * extend-select are pressed.
4658 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4659 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4666 _editor->rubberband_rect->hide();
4670 RubberbandSelectDrag::aborted (bool)
4672 _editor->rubberband_rect->hide ();
4675 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4676 : RegionDrag (e, i, p, v)
4678 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4682 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4684 Drag::start_grab (event, cursor);
4686 _editor->get_selection().add (_primary);
4688 framepos_t where = _primary->region()->position();
4689 setup_snap_delta (where);
4691 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4695 TimeFXDrag::motion (GdkEvent* event, bool)
4697 RegionView* rv = _primary;
4698 StreamView* cv = rv->get_time_axis_view().view ();
4700 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4701 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4702 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4703 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4704 _editor->snap_to_with_modifier (pf, event);
4705 pf -= snap_delta (event->button.state);
4707 if (pf > rv->region()->position()) {
4708 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4711 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4715 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4717 _primary->get_time_axis_view().hide_timestretch ();
4719 if (!movement_occurred) {
4723 if (last_pointer_frame() < _primary->region()->position()) {
4724 /* backwards drag of the left edge - not usable */
4728 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4730 float percentage = (double) newlen / (double) _primary->region()->length();
4732 #ifndef USE_RUBBERBAND
4733 // Soundtouch uses percentage / 100 instead of normal (/ 1)
4734 if (_primary->region()->data_type() == DataType::AUDIO) {
4735 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4739 if (!_editor->get_selection().regions.empty()) {
4740 /* primary will already be included in the selection, and edit
4741 group shared editing will propagate selection across
4742 equivalent regions, so just use the current region
4746 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4747 error << _("An error occurred while executing time stretch operation") << endmsg;
4753 TimeFXDrag::aborted (bool)
4755 _primary->get_time_axis_view().hide_timestretch ();
4758 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4761 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4765 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4767 Drag::start_grab (event);
4771 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4773 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4777 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4779 if (movement_occurred && _editor->session()) {
4780 /* make sure we stop */
4781 _editor->session()->request_transport_speed (0.0);
4786 ScrubDrag::aborted (bool)
4791 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4795 , _time_selection_at_start (!_editor->get_selection().time.empty())
4797 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4799 if (_time_selection_at_start) {
4800 start_at_start = _editor->get_selection().time.start();
4801 end_at_start = _editor->get_selection().time.end_frame();
4806 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4808 if (_editor->session() == 0) {
4812 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4814 switch (_operation) {
4815 case CreateSelection:
4816 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4821 cursor = _editor->cursors()->selector;
4822 Drag::start_grab (event, cursor);
4825 case SelectionStartTrim:
4826 if (_editor->clicked_axisview) {
4827 _editor->clicked_axisview->order_selection_trims (_item, true);
4829 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4832 case SelectionEndTrim:
4833 if (_editor->clicked_axisview) {
4834 _editor->clicked_axisview->order_selection_trims (_item, false);
4836 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4840 Drag::start_grab (event, cursor);
4843 case SelectionExtend:
4844 Drag::start_grab (event, cursor);
4848 if (_operation == SelectionMove) {
4849 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4851 show_verbose_cursor_time (adjusted_current_frame (event));
4856 SelectionDrag::setup_pointer_frame_offset ()
4858 switch (_operation) {
4859 case CreateSelection:
4860 _pointer_frame_offset = 0;
4863 case SelectionStartTrim:
4865 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4868 case SelectionEndTrim:
4869 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4872 case SelectionExtend:
4878 SelectionDrag::motion (GdkEvent* event, bool first_move)
4880 framepos_t start = 0;
4882 framecnt_t length = 0;
4883 framecnt_t distance = 0;
4885 framepos_t const pending_position = adjusted_current_frame (event);
4887 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4891 switch (_operation) {
4892 case CreateSelection:
4894 framepos_t grab = grab_frame ();
4897 grab = adjusted_current_frame (event, false);
4898 if (grab < pending_position) {
4899 _editor->snap_to (grab, RoundDownMaybe);
4901 _editor->snap_to (grab, RoundUpMaybe);
4905 if (pending_position < grab) {
4906 start = pending_position;
4909 end = pending_position;
4913 /* first drag: Either add to the selection
4914 or create a new selection
4921 /* adding to the selection */
4922 _editor->set_selected_track_as_side_effect (Selection::Add);
4923 _editor->clicked_selection = _editor->selection->add (start, end);
4930 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4931 _editor->set_selected_track_as_side_effect (Selection::Set);
4934 _editor->clicked_selection = _editor->selection->set (start, end);
4938 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4939 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4940 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4942 _editor->selection->add (atest);
4946 /* select all tracks within the rectangle that we've marked out so far */
4947 TrackViewList new_selection;
4948 TrackViewList& all_tracks (_editor->track_views);
4950 ArdourCanvas::Coord const top = grab_y();
4951 ArdourCanvas::Coord const bottom = current_pointer_y();
4953 if (top >= 0 && bottom >= 0) {
4955 //first, find the tracks that are covered in the y range selection
4956 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4957 if ((*i)->covered_by_y_range (top, bottom)) {
4958 new_selection.push_back (*i);
4962 //now find any tracks that are GROUPED with the tracks we selected
4963 TrackViewList grouped_add = new_selection;
4964 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4965 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4966 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4967 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4968 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4969 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4970 grouped_add.push_back (*j);
4975 //now compare our list with the current selection, and add or remove as necessary
4976 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4977 TrackViewList tracks_to_add;
4978 TrackViewList tracks_to_remove;
4979 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4980 if ( !_editor->selection->tracks.contains ( *i ) )
4981 tracks_to_add.push_back ( *i );
4982 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4983 if ( !grouped_add.contains ( *i ) )
4984 tracks_to_remove.push_back ( *i );
4985 _editor->selection->add(tracks_to_add);
4986 _editor->selection->remove(tracks_to_remove);
4992 case SelectionStartTrim:
4994 end = _editor->selection->time[_editor->clicked_selection].end;
4996 if (pending_position > end) {
4999 start = pending_position;
5003 case SelectionEndTrim:
5005 start = _editor->selection->time[_editor->clicked_selection].start;
5007 if (pending_position < start) {
5010 end = pending_position;
5017 start = _editor->selection->time[_editor->clicked_selection].start;
5018 end = _editor->selection->time[_editor->clicked_selection].end;
5020 length = end - start;
5021 distance = pending_position - start;
5022 start = pending_position;
5023 _editor->snap_to (start);
5025 end = start + length;
5029 case SelectionExtend:
5034 switch (_operation) {
5036 if (_time_selection_at_start) {
5037 _editor->selection->move_time (distance);
5041 _editor->selection->replace (_editor->clicked_selection, start, end);
5045 if (_operation == SelectionMove) {
5046 show_verbose_cursor_time(start);
5048 show_verbose_cursor_time(pending_position);
5053 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5055 Session* s = _editor->session();
5057 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5058 if (movement_occurred) {
5059 motion (event, false);
5060 /* XXX this is not object-oriented programming at all. ick */
5061 if (_editor->selection->time.consolidate()) {
5062 _editor->selection->TimeChanged ();
5065 /* XXX what if its a music time selection? */
5067 if (s->get_play_range() && s->transport_rolling()) {
5068 s->request_play_range (&_editor->selection->time, true);
5069 } else if (!s->config.get_external_sync()) {
5070 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5071 if (_operation == SelectionEndTrim)
5072 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5074 s->request_locate (_editor->get_selection().time.start());
5078 if (_editor->get_selection().time.length() != 0) {
5079 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5081 s->clear_range_selection ();
5086 /* just a click, no pointer movement.
5089 if (_operation == SelectionExtend) {
5090 if (_time_selection_at_start) {
5091 framepos_t pos = adjusted_current_frame (event, false);
5092 framepos_t start = min (pos, start_at_start);
5093 framepos_t end = max (pos, end_at_start);
5094 _editor->selection->set (start, end);
5097 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5098 if (_editor->clicked_selection) {
5099 _editor->selection->remove (_editor->clicked_selection);
5102 if (!_editor->clicked_selection) {
5103 _editor->selection->clear_time();
5108 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5109 _editor->selection->set (_editor->clicked_axisview);
5112 if (s && s->get_play_range () && s->transport_rolling()) {
5113 s->request_stop (false, false);
5118 _editor->stop_canvas_autoscroll ();
5119 _editor->clicked_selection = 0;
5120 _editor->commit_reversible_selection_op ();
5124 SelectionDrag::aborted (bool)
5129 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5130 : Drag (e, i, false),
5134 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5136 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5137 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5138 physical_screen_height (_editor->get_window())));
5139 _drag_rect->hide ();
5141 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5142 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5145 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5147 /* normal canvas items will be cleaned up when their parent group is deleted. But
5148 this item is created as the child of a long-lived parent group, and so we
5149 need to explicitly delete it.
5155 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5157 if (_editor->session() == 0) {
5161 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5163 if (!_editor->temp_location) {
5164 _editor->temp_location = new Location (*_editor->session());
5167 switch (_operation) {
5168 case CreateSkipMarker:
5169 case CreateRangeMarker:
5170 case CreateTransportMarker:
5171 case CreateCDMarker:
5173 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5178 cursor = _editor->cursors()->selector;
5182 Drag::start_grab (event, cursor);
5184 show_verbose_cursor_time (adjusted_current_frame (event));
5188 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5190 framepos_t start = 0;
5192 ArdourCanvas::Rectangle *crect;
5194 switch (_operation) {
5195 case CreateSkipMarker:
5196 crect = _editor->range_bar_drag_rect;
5198 case CreateRangeMarker:
5199 crect = _editor->range_bar_drag_rect;
5201 case CreateTransportMarker:
5202 crect = _editor->transport_bar_drag_rect;
5204 case CreateCDMarker:
5205 crect = _editor->cd_marker_bar_drag_rect;
5208 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5213 framepos_t const pf = adjusted_current_frame (event);
5215 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5216 framepos_t grab = grab_frame ();
5217 _editor->snap_to (grab);
5219 if (pf < grab_frame()) {
5227 /* first drag: Either add to the selection
5228 or create a new selection.
5233 _editor->temp_location->set (start, end);
5237 update_item (_editor->temp_location);
5239 //_drag_rect->raise_to_top();
5245 _editor->temp_location->set (start, end);
5247 double x1 = _editor->sample_to_pixel (start);
5248 double x2 = _editor->sample_to_pixel (end);
5252 update_item (_editor->temp_location);
5255 show_verbose_cursor_time (pf);
5260 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5262 Location * newloc = 0;
5266 if (movement_occurred) {
5267 motion (event, false);
5270 switch (_operation) {
5271 case CreateSkipMarker:
5272 case CreateRangeMarker:
5273 case CreateCDMarker:
5275 XMLNode &before = _editor->session()->locations()->get_state();
5276 if (_operation == CreateSkipMarker) {
5277 _editor->begin_reversible_command (_("new skip marker"));
5278 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5279 flags = Location::IsRangeMarker | Location::IsSkip;
5280 _editor->range_bar_drag_rect->hide();
5281 } else if (_operation == CreateCDMarker) {
5282 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5283 _editor->begin_reversible_command (_("new CD marker"));
5284 flags = Location::IsRangeMarker | Location::IsCDMarker;
5285 _editor->cd_marker_bar_drag_rect->hide();
5287 _editor->begin_reversible_command (_("new skip marker"));
5288 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5289 flags = Location::IsRangeMarker;
5290 _editor->range_bar_drag_rect->hide();
5292 newloc = new Location (
5293 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5296 _editor->session()->locations()->add (newloc, true);
5297 XMLNode &after = _editor->session()->locations()->get_state();
5298 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5299 _editor->commit_reversible_command ();
5303 case CreateTransportMarker:
5304 // popup menu to pick loop or punch
5305 _editor->new_transport_marker_context_menu (&event->button, _item);
5311 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5313 if (_operation == CreateTransportMarker) {
5315 /* didn't drag, so just locate */
5317 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5319 } else if (_operation == CreateCDMarker) {
5321 /* didn't drag, but mark is already created so do
5324 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5329 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5331 if (end == max_framepos) {
5332 end = _editor->session()->current_end_frame ();
5335 if (start == max_framepos) {
5336 start = _editor->session()->current_start_frame ();
5339 switch (_editor->mouse_mode) {
5341 /* find the two markers on either side and then make the selection from it */
5342 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5346 /* find the two markers on either side of the click and make the range out of it */
5347 _editor->selection->set (start, end);
5356 _editor->stop_canvas_autoscroll ();
5360 RangeMarkerBarDrag::aborted (bool movement_occured)
5362 if (movement_occured) {
5363 _drag_rect->hide ();
5368 RangeMarkerBarDrag::update_item (Location* location)
5370 double const x1 = _editor->sample_to_pixel (location->start());
5371 double const x2 = _editor->sample_to_pixel (location->end());
5373 _drag_rect->set_x0 (x1);
5374 _drag_rect->set_x1 (x2);
5377 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5379 , _cumulative_dx (0)
5380 , _cumulative_dy (0)
5381 , _was_selected (false)
5383 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5385 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5387 _region = &_primary->region_view ();
5388 _note_height = _region->midi_stream_view()->note_height ();
5392 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5394 Drag::start_grab (event);
5395 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5397 if (!(_was_selected = _primary->selected())) {
5399 /* tertiary-click means extend selection - we'll do that on button release,
5400 so don't add it here, because otherwise we make it hard to figure
5401 out the "extend-to" range.
5404 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5407 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5410 _region->note_selected (_primary, true);
5412 _editor->get_selection().clear_points();
5413 _region->unique_select (_primary);
5419 /** @return Current total drag x change in frames */
5421 NoteDrag::total_dx (const guint state) const
5424 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5426 /* primary note time */
5427 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5429 /* new time of the primary note in session frames */
5430 frameoffset_t st = n + dx + snap_delta (state);
5432 framepos_t const rp = _region->region()->position ();
5434 /* prevent the note being dragged earlier than the region's position */
5437 /* possibly snap and return corresponding delta */
5441 if (ArdourKeyboard::indicates_snap (state)) {
5442 if (_editor->snap_mode () != SnapOff) {
5446 if (_editor->snap_mode () == SnapOff) {
5448 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5449 if (ArdourKeyboard::indicates_snap_delta (state)) {
5457 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5458 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5460 ret = st - n - snap_delta (state);
5465 /** @return Current total drag y change in note number */
5467 NoteDrag::total_dy () const
5469 MidiStreamView* msv = _region->midi_stream_view ();
5470 double const y = _region->midi_view()->y_position ();
5471 /* new current note */
5472 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5474 n = max (msv->lowest_note(), n);
5475 n = min (msv->highest_note(), n);
5476 /* and work out delta */
5477 return n - msv->y_to_note (grab_y() - y);
5481 NoteDrag::motion (GdkEvent * event, bool)
5483 /* Total change in x and y since the start of the drag */
5484 frameoffset_t const dx = total_dx (event->button.state);
5485 int8_t const dy = total_dy ();
5487 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5488 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5489 double const tdy = -dy * _note_height - _cumulative_dy;
5492 _cumulative_dx += tdx;
5493 _cumulative_dy += tdy;
5495 int8_t note_delta = total_dy();
5497 _region->move_selection (tdx, tdy, note_delta);
5499 /* the new note value may be the same as the old one, but we
5500 * don't know what that means because the selection may have
5501 * involved more than one note and we might be doing something
5502 * odd with them. so show the note value anyway, always.
5506 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5508 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5509 (int) floor ((double)new_note));
5511 show_verbose_cursor_text (buf);
5516 NoteDrag::finished (GdkEvent* ev, bool moved)
5519 /* no motion - select note */
5521 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5522 _editor->current_mouse_mode() == Editing::MouseDraw) {
5524 bool changed = false;
5526 if (_was_selected) {
5527 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5529 _region->note_deselected (_primary);
5532 _editor->get_selection().clear_points();
5533 _region->unique_select (_primary);
5537 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5538 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5540 if (!extend && !add && _region->selection_size() > 1) {
5541 _editor->get_selection().clear_points();
5542 _region->unique_select (_primary);
5544 } else if (extend) {
5545 _region->note_selected (_primary, true, true);
5548 /* it was added during button press */
5555 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5556 _editor->commit_reversible_selection_op();
5560 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5565 NoteDrag::aborted (bool)
5570 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5571 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5572 : Drag (editor, atv->base_item ())
5574 , _y_origin (atv->y_position())
5575 , _nothing_to_drag (false)
5577 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5578 setup (atv->lines ());
5581 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5582 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5583 : Drag (editor, rv->get_canvas_group ())
5585 , _y_origin (rv->get_time_axis_view().y_position())
5586 , _nothing_to_drag (false)
5589 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5591 list<boost::shared_ptr<AutomationLine> > lines;
5593 AudioRegionView* audio_view;
5594 AutomationRegionView* automation_view;
5595 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5596 lines.push_back (audio_view->get_gain_line ());
5597 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5598 lines.push_back (automation_view->line ());
5601 error << _("Automation range drag created for invalid region type") << endmsg;
5607 /** @param lines AutomationLines to drag.
5608 * @param offset Offset from the session start to the points in the AutomationLines.
5611 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5613 /* find the lines that overlap the ranges being dragged */
5614 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5615 while (i != lines.end ()) {
5616 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5619 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5621 /* check this range against all the AudioRanges that we are using */
5622 list<AudioRange>::const_iterator k = _ranges.begin ();
5623 while (k != _ranges.end()) {
5624 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5630 /* add it to our list if it overlaps at all */
5631 if (k != _ranges.end()) {
5636 _lines.push_back (n);
5642 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5646 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5648 return 1.0 - ((global_y - _y_origin) / line->height());
5652 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5654 const double v = list->eval(x);
5655 return _integral ? rint(v) : v;
5659 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5661 Drag::start_grab (event, cursor);
5663 /* Get line states before we start changing things */
5664 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5665 i->state = &i->line->get_state ();
5666 i->original_fraction = y_fraction (i->line, current_pointer_y());
5669 if (_ranges.empty()) {
5671 /* No selected time ranges: drag all points */
5672 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5673 uint32_t const N = i->line->npoints ();
5674 for (uint32_t j = 0; j < N; ++j) {
5675 i->points.push_back (i->line->nth (j));
5681 if (_nothing_to_drag) {
5687 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5689 if (_nothing_to_drag && !first_move) {
5694 _editor->begin_reversible_command (_("automation range move"));
5696 if (!_ranges.empty()) {
5698 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5700 framecnt_t const half = (i->start + i->end) / 2;
5702 /* find the line that this audio range starts in */
5703 list<Line>::iterator j = _lines.begin();
5704 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5708 if (j != _lines.end()) {
5709 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5711 /* j is the line that this audio range starts in; fade into it;
5712 64 samples length plucked out of thin air.
5715 framepos_t a = i->start + 64;
5720 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5721 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5723 XMLNode &before = the_list->get_state();
5724 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5725 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5727 if (add_p || add_q) {
5728 _editor->session()->add_command (
5729 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5733 /* same thing for the end */
5736 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5740 if (j != _lines.end()) {
5741 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5743 /* j is the line that this audio range starts in; fade out of it;
5744 64 samples length plucked out of thin air.
5747 framepos_t b = i->end - 64;
5752 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5753 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5755 XMLNode &before = the_list->get_state();
5756 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5757 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5759 if (add_p || add_q) {
5760 _editor->session()->add_command (
5761 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5766 _nothing_to_drag = true;
5768 /* Find all the points that should be dragged and put them in the relevant
5769 points lists in the Line structs.
5772 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5774 uint32_t const N = i->line->npoints ();
5775 for (uint32_t j = 0; j < N; ++j) {
5777 /* here's a control point on this line */
5778 ControlPoint* p = i->line->nth (j);
5779 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5781 /* see if it's inside a range */
5782 list<AudioRange>::const_iterator k = _ranges.begin ();
5783 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5787 if (k != _ranges.end()) {
5788 /* dragging this point */
5789 _nothing_to_drag = false;
5790 i->points.push_back (p);
5796 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5797 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5801 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5802 float const f = y_fraction (l->line, current_pointer_y());
5803 /* we are ignoring x position for this drag, so we can just pass in anything */
5804 pair<double, float> result;
5806 result = l->line->drag_motion (0, f, true, false, ignored);
5807 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5812 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5814 if (_nothing_to_drag || !motion_occurred) {
5818 motion (event, false);
5819 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5820 i->line->end_drag (false, 0);
5823 _editor->commit_reversible_command ();
5827 AutomationRangeDrag::aborted (bool)
5829 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5834 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5836 , initial_time_axis_view (itav)
5838 /* note that time_axis_view may be null if the regionview was created
5839 * as part of a copy operation.
5841 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5842 layer = v->region()->layer ();
5843 initial_y = v->get_canvas_group()->position().y;
5844 initial_playlist = v->region()->playlist ();
5845 initial_position = v->region()->position ();
5846 initial_end = v->region()->position () + v->region()->length ();
5849 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5850 : Drag (e, i->canvas_item ())
5853 , _cumulative_dx (0)
5855 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5856 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5861 PatchChangeDrag::motion (GdkEvent* ev, bool)
5863 framepos_t f = adjusted_current_frame (ev);
5864 boost::shared_ptr<Region> r = _region_view->region ();
5865 f = max (f, r->position ());
5866 f = min (f, r->last_frame ());
5868 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5869 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5870 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5871 _cumulative_dx = dxu;
5875 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5877 if (!movement_occurred) {
5881 boost::shared_ptr<Region> r (_region_view->region ());
5882 framepos_t f = adjusted_current_frame (ev);
5883 f = max (f, r->position ());
5884 f = min (f, r->last_frame ());
5886 _region_view->move_patch_change (
5888 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5893 PatchChangeDrag::aborted (bool)
5895 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5899 PatchChangeDrag::setup_pointer_frame_offset ()
5901 boost::shared_ptr<Region> region = _region_view->region ();
5902 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5905 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5906 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5913 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5915 _region_view->update_drag_selection (
5917 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5921 MidiRubberbandSelectDrag::deselect_things ()
5926 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5927 : RubberbandSelectDrag (e, rv->get_canvas_group ())
5930 _vertical_only = true;
5934 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5936 double const y = _region_view->midi_view()->y_position ();
5938 y1 = max (0.0, y1 - y);
5939 y2 = max (0.0, y2 - y);
5941 _region_view->update_vertical_drag_selection (
5944 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5949 MidiVerticalSelectDrag::deselect_things ()
5954 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5955 : RubberbandSelectDrag (e, i)
5961 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5963 if (drag_in_progress) {
5964 /* We just want to select things at the end of the drag, not during it */
5968 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5970 _editor->begin_reversible_selection_op (X_("rubberband selection"));
5972 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5974 _editor->commit_reversible_selection_op ();
5978 EditorRubberbandSelectDrag::deselect_things ()
5980 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5982 _editor->selection->clear_tracks();
5983 _editor->selection->clear_regions();
5984 _editor->selection->clear_points ();
5985 _editor->selection->clear_lines ();
5986 _editor->selection->clear_midi_notes ();
5988 _editor->commit_reversible_selection_op();
5991 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5996 _note[0] = _note[1] = 0;
5999 NoteCreateDrag::~NoteCreateDrag ()
6005 NoteCreateDrag::grid_frames (framepos_t t) const
6008 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6010 grid_beats = Evoral::Beats(1);
6013 return _region_view->region_beats_to_region_frames (grid_beats);
6017 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6019 Drag::start_grab (event, cursor);
6021 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6023 framepos_t pf = _drags->current_pointer_frame ();
6024 framecnt_t const g = grid_frames (pf);
6026 /* Hack so that we always snap to the note that we are over, instead of snapping
6027 to the next one if we're more than halfway through the one we're over.
6029 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
6033 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
6034 _note[1] = _note[0];
6036 MidiStreamView* sv = _region_view->midi_stream_view ();
6037 double const x = _editor->sample_to_pixel (_note[0]);
6038 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6040 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6041 _drag_rect->set_outline_all ();
6042 _drag_rect->set_outline_color (0xffffff99);
6043 _drag_rect->set_fill_color (0xffffff66);
6047 NoteCreateDrag::motion (GdkEvent* event, bool)
6049 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
6050 double const x0 = _editor->sample_to_pixel (_note[0]);
6051 double const x1 = _editor->sample_to_pixel (_note[1]);
6052 _drag_rect->set_x0 (std::min(x0, x1));
6053 _drag_rect->set_x1 (std::max(x0, x1));
6057 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
6059 if (!had_movement) {
6063 framepos_t const start = min (_note[0], _note[1]);
6064 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6066 framecnt_t const g = grid_frames (start);
6067 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6069 if (_editor->snap_mode() == SnapNormal && length < g) {
6073 Evoral::Beats length_beats = max (
6074 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
6076 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
6080 NoteCreateDrag::y_to_region (double y) const
6083 _region_view->get_canvas_group()->canvas_to_item (x, y);
6088 NoteCreateDrag::aborted (bool)
6093 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6098 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6102 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6104 Drag::start_grab (event, cursor);
6108 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6114 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6117 distance = _drags->current_pointer_x() - grab_x();
6118 len = ar->fade_in()->back()->when;
6120 distance = grab_x() - _drags->current_pointer_x();
6121 len = ar->fade_out()->back()->when;
6124 /* how long should it be ? */
6126 new_length = len + _editor->pixel_to_sample (distance);
6128 /* now check with the region that this is legal */
6130 new_length = ar->verify_xfade_bounds (new_length, start);
6133 arv->reset_fade_in_shape_width (ar, new_length);
6135 arv->reset_fade_out_shape_width (ar, new_length);
6140 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6146 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6149 distance = _drags->current_pointer_x() - grab_x();
6150 len = ar->fade_in()->back()->when;
6152 distance = grab_x() - _drags->current_pointer_x();
6153 len = ar->fade_out()->back()->when;
6156 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6158 _editor->begin_reversible_command ("xfade trim");
6159 ar->playlist()->clear_owned_changes ();
6162 ar->set_fade_in_length (new_length);
6164 ar->set_fade_out_length (new_length);
6167 /* Adjusting the xfade may affect other regions in the playlist, so we need
6168 to get undo Commands from the whole playlist rather than just the
6172 vector<Command*> cmds;
6173 ar->playlist()->rdiff (cmds);
6174 _editor->session()->add_commands (cmds);
6175 _editor->commit_reversible_command ();
6180 CrossfadeEdgeDrag::aborted (bool)
6183 // arv->redraw_start_xfade ();
6185 // arv->redraw_end_xfade ();
6189 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6190 : Drag (e, item, true)
6191 , line (new EditorCursor (*e))
6193 line->set_position (pos);
6197 RegionCutDrag::~RegionCutDrag ()
6203 RegionCutDrag::motion (GdkEvent*, bool)
6205 framepos_t where = _drags->current_pointer_frame();
6206 _editor->snap_to (where);
6208 line->set_position (where);
6212 RegionCutDrag::finished (GdkEvent*, bool)
6214 _editor->get_track_canvas()->canvas()->re_enter();
6216 framepos_t pos = _drags->current_pointer_frame();
6220 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6226 _editor->split_regions_at (pos, rs);
6230 RegionCutDrag::aborted (bool)