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/profile.h"
41 #include "ardour/region_factory.h"
42 #include "ardour/session.h"
43 #include "ardour/session_playlists.h"
45 #include "canvas/canvas.h"
46 #include "canvas/scroll_group.h"
51 #include "audio_region_view.h"
52 #include "automation_region_view.h"
53 #include "midi_region_view.h"
54 #include "ardour_ui.h"
55 #include "gui_thread.h"
56 #include "control_point.h"
57 #include "region_gain_line.h"
58 #include "editor_drag.h"
59 #include "audio_time_axis.h"
60 #include "midi_time_axis.h"
61 #include "selection.h"
62 #include "midi_selection.h"
63 #include "automation_time_axis.h"
65 #include "editor_cursors.h"
66 #include "mouse_cursors.h"
67 #include "note_base.h"
68 #include "patch_change.h"
69 #include "ui_config.h"
70 #include "verbose_cursor.h"
73 using namespace ARDOUR;
76 using namespace Gtkmm2ext;
77 using namespace Editing;
78 using namespace ArdourCanvas;
80 using Gtkmm2ext::Keyboard;
82 double ControlPointDrag::_zero_gain_fraction = -1.0;
84 DragManager::DragManager (Editor* e)
87 , _current_pointer_x (0.0)
88 , _current_pointer_y (0.0)
89 , _current_pointer_frame (0)
90 , _old_follow_playhead (false)
94 DragManager::~DragManager ()
99 /** Call abort for each active drag */
101 DragManager::abort ()
105 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
110 if (!_drags.empty ()) {
111 _editor->set_follow_playhead (_old_follow_playhead, false);
115 _editor->abort_reversible_command();
121 DragManager::add (Drag* d)
123 d->set_manager (this);
124 _drags.push_back (d);
128 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
130 d->set_manager (this);
131 _drags.push_back (d);
136 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
138 /* Prevent follow playhead during the drag to be nice to the user */
139 _old_follow_playhead = _editor->follow_playhead ();
140 _editor->set_follow_playhead (false);
142 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
144 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
145 (*i)->start_grab (e, c);
149 /** Call end_grab for each active drag.
150 * @return true if any drag reported movement having occurred.
153 DragManager::end_grab (GdkEvent* e)
158 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
159 bool const t = (*i)->end_grab (e);
170 _editor->set_follow_playhead (_old_follow_playhead, false);
176 DragManager::mark_double_click ()
178 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
179 (*i)->set_double_click (true);
184 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
188 /* calling this implies that we expect the event to have canvas
191 * Can we guarantee that this is true?
194 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
196 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
197 bool const t = (*i)->motion_handler (e, from_autoscroll);
198 /* run all handlers; return true if at least one of them
199 returns true (indicating that the event has been handled).
211 DragManager::have_item (ArdourCanvas::Item* i) const
213 list<Drag*>::const_iterator j = _drags.begin ();
214 while (j != _drags.end() && (*j)->item () != i) {
218 return j != _drags.end ();
221 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
225 , _pointer_frame_offset (0)
226 , _x_constrained (false)
227 , _y_constrained (false)
228 , _was_rolling (false)
229 , _trackview_only (trackview_only)
230 , _move_threshold_passed (false)
231 , _starting_point_passed (false)
232 , _initially_vertical (false)
233 , _was_double_click (false)
236 , _last_pointer_x (0.0)
237 , _last_pointer_y (0.0)
238 , _raw_grab_frame (0)
240 , _last_pointer_frame (0)
242 , _snap_delta_music (0.0)
243 , _constraint_pressed (false)
249 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
255 _cursor_ctx = CursorContext::create (*_editor, cursor);
257 _cursor_ctx->change (cursor);
264 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
267 /* we set up x/y dragging constraints on first move */
268 _constraint_pressed = ArdourKeyboard::indicates_constraint (event->button.state);
270 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
272 setup_pointer_frame_offset ();
273 _grab_frame = adjusted_frame (_raw_grab_frame, event).frame;
274 _last_pointer_frame = _grab_frame;
275 _last_pointer_x = _grab_x;
277 if (_trackview_only) {
278 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
281 _last_pointer_y = _grab_y;
285 if (!_editor->cursors()->is_invalid (cursor)) {
286 /* CAIROCANVAS need a variant here that passes *cursor */
287 _cursor_ctx = CursorContext::create (*_editor, cursor);
290 if (_editor->session() && _editor->session()->transport_rolling()) {
293 _was_rolling = false;
296 switch (_editor->snap_type()) {
297 case SnapToRegionStart:
298 case SnapToRegionEnd:
299 case SnapToRegionSync:
300 case SnapToRegionBoundary:
301 _editor->build_region_boundary_cache ();
308 /** Call to end a drag `successfully'. Ungrabs item and calls
309 * subclass' finished() method.
311 * @param event GDK event, or 0.
312 * @return true if some movement occurred, otherwise false.
315 Drag::end_grab (GdkEvent* event)
317 _editor->stop_canvas_autoscroll ();
321 finished (event, _move_threshold_passed);
323 _editor->verbose_cursor()->hide ();
326 return _move_threshold_passed;
330 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
332 MusicFrame pos (0, 0);
334 if (f > _pointer_frame_offset) {
335 pos.frame = f - _pointer_frame_offset;
339 _editor->snap_to_with_modifier (pos, event);
346 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
348 return adjusted_frame (_drags->current_pointer_frame (), event, snap).frame;
352 Drag::snap_delta (guint state) const
354 if (ArdourKeyboard::indicates_snap_delta (state)) {
361 Drag::snap_delta_music (guint state) const
363 if (ArdourKeyboard::indicates_snap_delta (state)) {
364 return _snap_delta_music;
371 Drag::current_pointer_x() const
373 return _drags->current_pointer_x ();
377 Drag::current_pointer_y () const
379 if (!_trackview_only) {
380 return _drags->current_pointer_y ();
383 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
387 Drag::setup_snap_delta (MusicFrame pos)
389 TempoMap& map (_editor->session()->tempo_map());
390 MusicFrame snap (pos);
391 _editor->snap_to (snap, ARDOUR::RoundNearest, false, true);
392 _snap_delta = snap.frame - pos.frame;
394 _snap_delta_music = 0.0;
396 if (_snap_delta != 0) {
397 _snap_delta_music = map.exact_qn_at_frame (snap.frame, snap.division) - map.exact_qn_at_frame (pos.frame, pos.division);
402 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
404 /* check to see if we have moved in any way that matters since the last motion event */
405 if (_move_threshold_passed &&
406 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
407 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
411 pair<framecnt_t, int> const threshold = move_threshold ();
413 bool const old_move_threshold_passed = _move_threshold_passed;
415 if (!_move_threshold_passed) {
417 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
418 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
420 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
423 if (active (_editor->mouse_mode) && _move_threshold_passed) {
425 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
427 if (old_move_threshold_passed != _move_threshold_passed) {
431 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
432 _initially_vertical = true;
434 _initially_vertical = false;
436 /** check constraints for this drag.
437 * Note that the current convention is to use "contains" for
438 * key modifiers during motion and "equals" when initiating a drag.
439 * In this case we haven't moved yet, so "equals" applies here.
441 if (Config->get_edit_mode() != Lock) {
442 if (event->motion.state & Gdk::BUTTON2_MASK) {
443 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
444 if (_constraint_pressed) {
445 _x_constrained = false;
446 _y_constrained = true;
448 _x_constrained = true;
449 _y_constrained = false;
451 } else if (_constraint_pressed) {
452 // if dragging normally, the motion is constrained to the first direction of movement.
453 if (_initially_vertical) {
454 _x_constrained = true;
455 _y_constrained = false;
457 _x_constrained = false;
458 _y_constrained = true;
462 if (event->button.state & Gdk::BUTTON2_MASK) {
463 _x_constrained = false;
465 _x_constrained = true;
467 _y_constrained = false;
471 if (!from_autoscroll) {
472 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
475 if (!_editor->autoscroll_active() || from_autoscroll) {
478 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
480 motion (event, first_move && !_starting_point_passed);
482 if (first_move && !_starting_point_passed) {
483 _starting_point_passed = true;
486 _last_pointer_x = _drags->current_pointer_x ();
487 _last_pointer_y = current_pointer_y ();
488 _last_pointer_frame = adjusted_current_frame (event, false);
498 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
506 aborted (_move_threshold_passed);
508 _editor->stop_canvas_autoscroll ();
509 _editor->verbose_cursor()->hide ();
513 Drag::show_verbose_cursor_time (framepos_t frame)
515 _editor->verbose_cursor()->set_time (frame);
516 _editor->verbose_cursor()->show ();
520 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
522 _editor->verbose_cursor()->set_duration (start, end);
523 _editor->verbose_cursor()->show ();
527 Drag::show_verbose_cursor_text (string const & text)
529 _editor->verbose_cursor()->set (text);
530 _editor->verbose_cursor()->show ();
533 boost::shared_ptr<Region>
534 Drag::add_midi_region (MidiTimeAxisView* view, bool commit)
536 if (_editor->session()) {
537 const TempoMap& map (_editor->session()->tempo_map());
538 framecnt_t pos = grab_frame();
539 /* not that the frame rate used here can be affected by pull up/down which
542 framecnt_t len = map.frame_at_beat (max (0.0, map.beat_at_frame (pos)) + 1.0) - pos;
543 return view->add_region (grab_frame(), len, commit);
546 return boost::shared_ptr<Region>();
549 struct PresentationInfoTimeAxisViewSorter {
550 bool operator() (TimeAxisView* a, TimeAxisView* b) {
551 return a->stripable()->presentation_info().order() < b->stripable()->presentation_info().order();
555 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
560 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
562 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
563 as some of the regions we are dragging may be on such tracks.
566 TrackViewList track_views = _editor->track_views;
567 track_views.sort (PresentationInfoTimeAxisViewSorter ());
569 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
570 _time_axis_views.push_back (*i);
572 TimeAxisView::Children children_list = (*i)->get_child_list ();
573 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
574 _time_axis_views.push_back (j->get());
578 /* the list of views can be empty at this point if this is a region list-insert drag
581 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
582 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
585 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
589 RegionDrag::region_going_away (RegionView* v)
591 list<DraggingView>::iterator i = _views.begin ();
592 while (i != _views.end() && i->view != v) {
596 if (i != _views.end()) {
601 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
602 * or -1 if it is not found.
605 RegionDrag::find_time_axis_view (TimeAxisView* t) const
608 int const N = _time_axis_views.size ();
609 while (i < N && _time_axis_views[i] != t) {
613 if (_time_axis_views[i] != t) {
620 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
621 : RegionDrag (e, i, p, v)
623 , _ignore_video_lock (false)
624 , _last_position (0, 0)
626 , _last_pointer_time_axis_view (0)
627 , _last_pointer_layer (0)
632 _last_position = MusicFrame (_primary->region()->position(), 0);
633 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
637 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
639 Drag::start_grab (event, cursor);
640 setup_snap_delta (_last_position);
642 show_verbose_cursor_time (_last_position.frame);
644 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
646 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
647 assert(_last_pointer_time_axis_view >= 0);
648 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
651 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
652 _ignore_video_lock = true;
656 /* cross track dragging seems broken here. disabled for now. */
657 _y_constrained = true;
662 RegionMotionDrag::compute_x_delta (GdkEvent const * event, MusicFrame* pending_region_position)
664 /* compute the amount of pointer motion in frames, and where
665 the region would be if we moved it by that much.
667 if (_x_constrained) {
668 *pending_region_position = _last_position;
672 *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
674 framecnt_t sync_offset;
677 sync_offset = _primary->region()->sync_offset (sync_dir);
679 /* we don't handle a sync point that lies before zero.
681 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position->frame >= sync_offset)) {
683 framecnt_t const sd = snap_delta (event->button.state);
684 MusicFrame sync_snap (pending_region_position->frame + (sync_dir * sync_offset) + sd, 0);
685 _editor->snap_to_with_modifier (sync_snap, event);
686 if (sync_offset == 0 && sd == 0) {
687 *pending_region_position = sync_snap;
689 pending_region_position->set (_primary->region()->adjust_to_sync (sync_snap.frame) - sd, 0);
692 *pending_region_position = _last_position;
695 if (pending_region_position->frame > max_framepos - _primary->region()->length()) {
696 *pending_region_position = _last_position;
701 bool const x_move_allowed = !_x_constrained;
703 if ((pending_region_position->frame != _last_position.frame) && x_move_allowed) {
705 /* x movement since last time (in pixels) */
706 dx = _editor->sample_to_pixel_unrounded (pending_region_position->frame - _last_position.frame);
708 /* total x movement */
709 framecnt_t total_dx = _editor->pixel_to_sample (_total_x_delta + dx);
711 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
712 frameoffset_t const off = i->view->region()->position() + total_dx;
714 dx = dx - _editor->sample_to_pixel_unrounded (off);
715 *pending_region_position = MusicFrame (pending_region_position->frame - off, 0);
725 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
731 const int tavsize = _time_axis_views.size();
732 const int dt = delta > 0 ? +1 : -1;
734 int target = start + delta - skip;
736 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
737 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
739 while (current >= 0 && current != target) {
741 if (current < 0 && dt < 0) {
744 if (current >= tavsize && dt > 0) {
747 if (current < 0 || current >= tavsize) {
751 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
752 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
756 if (distance_only && current == start + delta) {
764 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
766 if (_y_constrained) {
770 const int tavsize = _time_axis_views.size();
771 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
772 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
773 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
775 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
776 /* already in the drop zone */
777 if (delta_track >= 0) {
778 /* downward motion - OK if others are still not in the dropzone */
787 } else if (n >= tavsize) {
788 /* downward motion into drop zone. That's fine. */
792 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
793 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
794 /* not a track, or the wrong type */
798 double const l = i->layer + delta_layer;
800 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
801 mode to allow the user to place a region below another on layer 0.
803 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
804 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
805 If it has, the layers will be munged later anyway, so it's ok.
811 /* all regions being dragged are ok with this change */
815 struct DraggingViewSorter {
816 bool operator() (const DraggingView& a, const DraggingView& b) {
817 return a.time_axis_view < b.time_axis_view;
822 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
824 double delta_layer = 0;
825 int delta_time_axis_view = 0;
826 int current_pointer_time_axis_view = -1;
828 assert (!_views.empty ());
830 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
832 /* Find the TimeAxisView that the pointer is now over */
833 const double cur_y = current_pointer_y ();
834 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
835 TimeAxisView* tv = r.first;
837 if (!tv && cur_y < 0) {
838 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
842 /* find drop-zone y-position */
843 Coord last_track_bottom_edge;
844 last_track_bottom_edge = 0;
845 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
846 if (!(*t)->hidden()) {
847 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
852 if (tv && tv->view()) {
853 /* the mouse is over a track */
854 double layer = r.second;
856 if (first_move && tv->view()->layer_display() == Stacked) {
857 tv->view()->set_layer_display (Expanded);
860 /* Here's the current pointer position in terms of time axis view and layer */
861 current_pointer_time_axis_view = find_time_axis_view (tv);
862 assert(current_pointer_time_axis_view >= 0);
864 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
866 /* Work out the change in y */
868 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
869 if (!rtv || !rtv->is_track()) {
870 /* ignore non-tracks early on. we can't move any regions on them */
871 } else if (_last_pointer_time_axis_view < 0) {
872 /* Was in the drop-zone, now over a track.
873 * Hence it must be an upward move (from the bottom)
875 * track_index is still -1, so delta must be set to
876 * move up the correct number of tracks from the bottom.
878 * This is necessary because steps may be skipped if
879 * the bottom-most track is not a valid target and/or
880 * if there are hidden tracks at the bottom.
881 * Hence the initial offset (_ddropzone) as well as the
882 * last valid pointer position (_pdropzone) need to be
883 * taken into account.
885 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
887 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
890 /* TODO needs adjustment per DraggingView,
892 * e.g. select one region on the top-layer of a track
893 * and one region which is at the bottom-layer of another track
896 * Indicated drop-zones and layering is wrong.
897 * and may infer additional layers on the target-track
898 * (depending how many layers the original track had).
900 * Or select two regions (different layers) on a same track,
901 * move across a non-layer track.. -> layering info is lost.
902 * on drop either of the regions may be on top.
904 * Proposed solution: screw it :) well,
905 * don't use delta_layer, use an absolute value
906 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
907 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
908 * 3) iterate over all DraggingView, find the one that is over the track with most layers
909 * 4) proportionally scale layer to layers available on target
911 delta_layer = current_pointer_layer - _last_pointer_layer;
914 /* for automation lanes, there is a TimeAxisView but no ->view()
915 * if (!tv) -> dropzone
917 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
918 /* Moving into the drop-zone.. */
919 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
920 /* delta_time_axis_view may not be sufficient to move into the DZ
921 * the mouse may enter it, but it may not be a valid move due to
924 * -> remember the delta needed to move into the dropzone
926 _ddropzone = delta_time_axis_view;
927 /* ..but subtract hidden tracks (or routes) at the bottom.
928 * we silently move mover them
930 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
931 - _time_axis_views.size();
933 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
934 /* move around inside the zone.
935 * This allows to move further down until all regions are in the zone.
937 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
938 assert(ptr_y >= last_track_bottom_edge);
939 assert(_ddropzone > 0);
941 /* calculate mouse position in 'tracks' below last track. */
942 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
943 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
945 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
947 delta_time_axis_view = dzpos - _pdropzone;
948 } else if (dzpos < _pdropzone && _ndropzone > 0) {
949 // move up inside the DZ
950 delta_time_axis_view = dzpos - _pdropzone;
954 /* Work out the change in x */
955 TempoMap& tmap = _editor->session()->tempo_map();
956 MusicFrame pending_region_position (0, 0);
957 double const x_delta = compute_x_delta (event, &pending_region_position);
959 double const last_pos_qn = tmap.exact_qn_at_frame (_last_position.frame, _last_position.division);
960 double const qn_delta = tmap.exact_qn_at_frame (pending_region_position.frame, pending_region_position.division) - last_pos_qn;
962 _last_position = pending_region_position;
964 /* calculate hidden tracks in current y-axis delta */
966 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
967 /* The mouse is more than one track below the dropzone.
968 * distance calculation is not needed (and would not work, either
969 * because the dropzone is "packed").
971 * Except when [partially] moving regions out of dropzone in a large step.
972 * (the mouse may or may not remain in the DZ)
973 * Hidden tracks at the bottom of the TAV need to be skipped.
975 * This also handles the case if the mouse entered the DZ
976 * in a large step (exessive delta), either due to fast-movement,
977 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
979 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
980 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
982 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
983 -_time_axis_views.size() - dt;
986 else if (_last_pointer_time_axis_view < 0) {
987 /* Moving out of the zone. Check for hidden tracks at the bottom. */
988 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
989 -_time_axis_views.size() - delta_time_axis_view;
991 /* calculate hidden tracks that are skipped by the pointer movement */
992 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
993 - _last_pointer_time_axis_view
994 - delta_time_axis_view;
997 /* Verify change in y */
998 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
999 /* this y movement is not allowed, so do no y movement this time */
1000 delta_time_axis_view = 0;
1005 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
1006 /* haven't reached next snap point, and we're not switching
1007 trackviews nor layers. nothing to do.
1012 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
1013 PlaylistDropzoneMap playlist_dropzone_map;
1014 _ndropzone = 0; // number of elements currently in the dropzone
1017 /* sort views by time_axis.
1018 * This retains track order in the dropzone, regardless
1019 * of actual selection order
1021 _views.sort (DraggingViewSorter());
1023 /* count number of distinct tracks of all regions
1024 * being dragged, used for dropzone.
1026 int prev_track = -1;
1027 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1028 if (i->time_axis_view != prev_track) {
1029 prev_track = i->time_axis_view;
1035 _views.back().time_axis_view -
1036 _views.front().time_axis_view;
1038 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1039 - _views.back().time_axis_view;
1041 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1045 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1047 RegionView* rv = i->view;
1052 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1059 /* reparent the regionview into a group above all
1063 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1064 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1065 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1066 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1067 /* move the item so that it continues to appear at the
1068 same location now that its parent has changed.
1070 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1073 /* If we have moved tracks, we'll fudge the layer delta so that the
1074 region gets moved back onto layer 0 on its new track; this avoids
1075 confusion when dragging regions from non-zero layers onto different
1078 double this_delta_layer = delta_layer;
1079 if (delta_time_axis_view != 0) {
1080 this_delta_layer = - i->layer;
1083 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1085 int track_index = i->time_axis_view + this_delta_time_axis_view;
1086 assert(track_index >= 0);
1088 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1089 /* Track is in the Dropzone */
1091 i->time_axis_view = track_index;
1092 assert(i->time_axis_view >= (int) _time_axis_views.size());
1095 double yposition = 0;
1096 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1097 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1100 /* store index of each new playlist as a negative count, starting at -1 */
1102 if (pdz == playlist_dropzone_map.end()) {
1103 /* compute where this new track (which doesn't exist yet) will live
1106 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1108 /* How high is this region view ? */
1110 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1111 ArdourCanvas::Rect bbox;
1114 bbox = obbox.get ();
1117 last_track_bottom_edge += bbox.height();
1119 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1122 yposition = pdz->second;
1125 /* values are zero or negative, hence the use of min() */
1126 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1129 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1131 mrv->apply_note_range (60, 71, true);
1135 /* The TimeAxisView that this region is now over */
1136 TimeAxisView* current_tv = _time_axis_views[track_index];
1138 /* Ensure it is moved from stacked -> expanded if appropriate */
1139 if (current_tv->view()->layer_display() == Stacked) {
1140 current_tv->view()->set_layer_display (Expanded);
1143 /* We're only allowed to go -ve in layer on Expanded views */
1144 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1145 this_delta_layer = - i->layer;
1149 rv->set_height (current_tv->view()->child_height ());
1151 /* Update show/hidden status as the region view may have come from a hidden track,
1152 or have moved to one.
1154 if (current_tv->hidden ()) {
1155 rv->get_canvas_group()->hide ();
1157 rv->get_canvas_group()->show ();
1160 /* Update the DraggingView */
1161 i->time_axis_view = track_index;
1162 i->layer += this_delta_layer;
1165 _editor->mouse_brush_insert_region (rv, pending_region_position.frame);
1169 /* Get the y coordinate of the top of the track that this region is now over */
1170 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1172 /* And adjust for the layer that it should be on */
1173 StreamView* cv = current_tv->view ();
1174 switch (cv->layer_display ()) {
1178 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1181 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1185 /* need to get the parent of the regionview
1186 * canvas group and get its position in
1187 * equivalent coordinate space as the trackview
1188 * we are now dragging over.
1191 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1195 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1197 MidiStreamView* msv;
1198 if ((msv = dynamic_cast <MidiStreamView*> (current_tv->view())) != 0) {
1199 mrv->apply_note_range (msv->lowest_note(), msv->highest_note(), true);
1204 /* Now move the region view */
1205 if (rv->region()->position_lock_style() == MusicTime) {
1206 double const last_qn = tmap.quarter_note_at_frame (rv->get_position());
1207 framepos_t const x_pos_music = tmap.frame_at_quarter_note (last_qn + qn_delta);
1209 rv->set_position (x_pos_music, 0);
1210 rv->move (0, y_delta);
1212 rv->move (x_delta, y_delta);
1215 } /* foreach region */
1217 _total_x_delta += x_delta;
1219 if (x_delta != 0 && !_brushing) {
1220 show_verbose_cursor_time (_last_position.frame);
1223 /* keep track of pointer movement */
1225 /* the pointer is currently over a time axis view */
1227 if (_last_pointer_time_axis_view < 0) {
1228 /* last motion event was not over a time axis view
1229 * or last y-movement out of the dropzone was not valid
1232 if (delta_time_axis_view < 0) {
1233 /* in the drop zone, moving up */
1235 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1236 * We do not use negative _last_pointer_time_axis_view because
1237 * the dropzone is "packed" (the actual track offset is ignored)
1239 * As opposed to the actual number
1240 * of elements in the dropzone (_ndropzone)
1241 * _pdropzone is not constrained. This is necessary
1242 * to allow moving multiple regions with y-distance
1245 * There can be 0 elements in the dropzone,
1246 * even though the drag-pointer is inside the DZ.
1249 * [ Audio-track, Midi-track, Audio-track, DZ ]
1250 * move regions from both audio tracks at the same time into the
1251 * DZ by grabbing the region in the bottom track.
1253 assert(current_pointer_time_axis_view >= 0);
1254 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1258 /* only move out of the zone if the movement is OK */
1259 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1260 assert(delta_time_axis_view < 0);
1261 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1262 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1263 * the current position can be calculated as follows:
1265 // a well placed oofus attack can still throw this off.
1266 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1267 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1270 /* last motion event was also over a time axis view */
1271 _last_pointer_time_axis_view += delta_time_axis_view;
1272 assert(_last_pointer_time_axis_view >= 0);
1277 /* the pointer is not over a time axis view */
1278 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1279 _pdropzone += delta_time_axis_view - delta_skip;
1280 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1283 _last_pointer_layer += delta_layer;
1287 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1289 if (_copy && first_move) {
1290 if (_x_constrained && !_brushing) {
1291 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1292 } else if (!_brushing) {
1293 _editor->begin_reversible_command (Operations::region_copy);
1294 } else if (_brushing) {
1295 _editor->begin_reversible_command (Operations::drag_region_brush);
1297 /* duplicate the regionview(s) and region(s) */
1299 list<DraggingView> new_regionviews;
1301 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1303 RegionView* rv = i->view;
1304 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1305 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1307 const boost::shared_ptr<const Region> original = rv->region();
1308 boost::shared_ptr<Region> region_copy;
1310 region_copy = RegionFactory::create (original, true);
1312 /* need to set this so that the drop zone code can work. This doesn't
1313 actually put the region into the playlist, but just sets a weak pointer
1316 region_copy->set_playlist (original->playlist());
1320 boost::shared_ptr<AudioRegion> audioregion_copy
1321 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1323 nrv = new AudioRegionView (*arv, audioregion_copy);
1325 boost::shared_ptr<MidiRegion> midiregion_copy
1326 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1327 nrv = new MidiRegionView (*mrv, midiregion_copy);
1332 nrv->get_canvas_group()->show ();
1333 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1335 /* swap _primary to the copy */
1337 if (rv == _primary) {
1341 /* ..and deselect the one we copied */
1343 rv->set_selected (false);
1346 if (!new_regionviews.empty()) {
1348 /* reflect the fact that we are dragging the copies */
1350 _views = new_regionviews;
1352 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1355 } else if (!_copy && first_move) {
1356 if (_x_constrained && !_brushing) {
1357 _editor->begin_reversible_command (_("fixed time region drag"));
1358 } else if (!_brushing) {
1359 _editor->begin_reversible_command (Operations::region_drag);
1360 } else if (_brushing) {
1361 _editor->begin_reversible_command (Operations::drag_region_brush);
1364 RegionMotionDrag::motion (event, first_move);
1368 RegionMotionDrag::finished (GdkEvent *, bool)
1370 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1371 if (!(*i)->view()) {
1375 if ((*i)->view()->layer_display() == Expanded) {
1376 (*i)->view()->set_layer_display (Stacked);
1382 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1384 RegionMotionDrag::finished (ev, movement_occurred);
1386 if (!movement_occurred) {
1390 if (was_double_click() && !_views.empty()) {
1391 DraggingView dv = _views.front();
1392 _editor->edit_region (dv.view);
1398 assert (!_views.empty ());
1400 /* We might have hidden region views so that they weren't visible during the drag
1401 (when they have been reparented). Now everything can be shown again, as region
1402 views are back in their track parent groups.
1404 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1405 i->view->get_canvas_group()->show ();
1408 bool const changed_position = (_last_position.frame != _primary->region()->position());
1409 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1433 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1435 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1438 TimeAxisView* tav = 0;
1440 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1441 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1442 uint32_t output_chan = region->n_channels();
1443 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1444 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1446 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
1447 tav =_editor->axis_view_from_stripable (audio_tracks.front());
1449 ChanCount one_midi_port (DataType::MIDI, 1);
1450 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1451 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port,
1452 Config->get_strict_io () || Profile->get_mixbus (),
1453 boost::shared_ptr<ARDOUR::PluginInfo>(),
1454 (ARDOUR::Plugin::PresetRecord*) 0,
1455 (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
1456 tav = _editor->axis_view_from_stripable (midi_tracks.front());
1460 tav->set_height (original->current_height());
1463 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1466 return dynamic_cast<RouteTimeAxisView*> (tav);
1470 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, MusicFrame last_position, int32_t const ev_state)
1472 RegionSelection new_views;
1473 PlaylistSet modified_playlists;
1474 RouteTimeAxisView* new_time_axis_view = 0;
1475 framecnt_t const drag_delta = _primary->region()->position() - _last_position.frame;
1477 TempoMap& tmap (_editor->session()->tempo_map());
1478 const double last_pos_qn = tmap.exact_qn_at_frame (last_position.frame, last_position.division);
1479 const double qn_delta = _primary->region()->quarter_note() - last_pos_qn;
1482 /* all changes were made during motion event handlers */
1484 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1488 _editor->commit_reversible_command ();
1492 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1493 PlaylistMapping playlist_mapping;
1495 /* insert the regions into their new playlists */
1496 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1498 RouteTimeAxisView* dest_rtv = 0;
1500 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1504 MusicFrame where (0, 0);
1505 double quarter_note;
1507 if (changed_position && !_x_constrained) {
1508 where.set (i->view->region()->position() - drag_delta, 0);
1509 quarter_note = i->view->region()->quarter_note() - qn_delta;
1511 /* region has not moved - divisor will not affect musical pos */
1512 where.set (i->view->region()->position(), 0);
1513 quarter_note = i->view->region()->quarter_note();
1516 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1517 /* dragged to drop zone */
1519 PlaylistMapping::iterator pm;
1521 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1522 /* first region from this original playlist: create a new track */
1523 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1524 if(!new_time_axis_view) {
1528 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1529 dest_rtv = new_time_axis_view;
1531 /* we already created a new track for regions from this playlist, use it */
1532 dest_rtv = pm->second;
1535 /* destination time axis view is the one we dragged to */
1536 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1539 if (dest_rtv != 0) {
1540 RegionView* new_view;
1541 if (i->view == _primary && !_x_constrained) {
1542 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, last_position, last_pos_qn,
1543 modified_playlists, true);
1545 if (i->view->region()->position_lock_style() == AudioTime) {
1546 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, quarter_note,
1547 modified_playlists);
1549 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, quarter_note,
1550 modified_playlists, true);
1554 if (new_view != 0) {
1555 new_views.push_back (new_view);
1559 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1560 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1563 list<DraggingView>::const_iterator next = i;
1569 /* If we've created new regions either by copying or moving
1570 to a new track, we want to replace the old selection with the new ones
1573 if (new_views.size() > 0) {
1574 _editor->selection->set (new_views);
1577 /* write commands for the accumulated diffs for all our modified playlists */
1578 add_stateful_diff_commands_for_playlists (modified_playlists);
1580 _editor->commit_reversible_command ();
1584 RegionMoveDrag::finished_no_copy (
1585 bool const changed_position,
1586 bool const changed_tracks,
1587 MusicFrame last_position,
1588 int32_t const ev_state
1591 RegionSelection new_views;
1592 PlaylistSet modified_playlists;
1593 PlaylistSet frozen_playlists;
1594 set<RouteTimeAxisView*> views_to_update;
1595 RouteTimeAxisView* new_time_axis_view = 0;
1596 framecnt_t const drag_delta = _primary->region()->position() - last_position.frame;
1598 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1599 PlaylistMapping playlist_mapping;
1601 TempoMap& tmap (_editor->session()->tempo_map());
1602 const double last_pos_qn = tmap.exact_qn_at_frame (last_position.frame, last_position.division);
1603 const double qn_delta = _primary->region()->quarter_note() - last_pos_qn;
1605 std::set<boost::shared_ptr<const Region> > uniq;
1606 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1608 RegionView* rv = i->view;
1609 RouteTimeAxisView* dest_rtv = 0;
1611 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1616 if (uniq.find (rv->region()) != uniq.end()) {
1617 /* prevent duplicate moves when selecting regions from shared playlists */
1621 uniq.insert(rv->region());
1623 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1624 /* dragged to drop zone */
1626 PlaylistMapping::iterator pm;
1628 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1629 /* first region from this original playlist: create a new track */
1630 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1631 if(!new_time_axis_view) { // New track creation failed
1635 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1636 dest_rtv = new_time_axis_view;
1638 /* we already created a new track for regions from this playlist, use it */
1639 dest_rtv = pm->second;
1643 /* destination time axis view is the one we dragged to */
1644 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1649 double const dest_layer = i->layer;
1651 views_to_update.insert (dest_rtv);
1653 MusicFrame where (0, 0);
1654 double quarter_note;
1656 if (changed_position && !_x_constrained) {
1657 where.set (rv->region()->position() - drag_delta, 0);
1658 quarter_note = i->view->region()->quarter_note() - qn_delta;
1660 where.set (rv->region()->position(), 0);
1661 quarter_note = i->view->region()->quarter_note();
1664 if (changed_tracks) {
1666 /* insert into new playlist */
1667 RegionView* new_view;
1668 if (rv == _primary && !_x_constrained) {
1669 new_view = insert_region_into_playlist (
1670 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, last_position, last_pos_qn,
1671 modified_playlists, true
1674 if (rv->region()->position_lock_style() == AudioTime) {
1676 new_view = insert_region_into_playlist (
1677 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, quarter_note,
1681 new_view = insert_region_into_playlist (
1682 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, quarter_note,
1683 modified_playlists, true
1688 if (new_view == 0) {
1693 new_views.push_back (new_view);
1695 /* remove from old playlist */
1697 /* the region that used to be in the old playlist is not
1698 moved to the new one - we use a copy of it. as a result,
1699 any existing editor for the region should no longer be
1702 rv->hide_region_editor();
1705 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1709 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1711 /* this movement may result in a crossfade being modified, or a layering change,
1712 so we need to get undo data from the playlist as well as the region.
1715 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1717 playlist->clear_changes ();
1720 rv->region()->clear_changes ();
1723 motion on the same track. plonk the previously reparented region
1724 back to its original canvas group (its streamview).
1725 No need to do anything for copies as they are fake regions which will be deleted.
1728 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1729 rv->get_canvas_group()->set_y_position (i->initial_y);
1732 /* just change the model */
1733 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1734 playlist->set_layer (rv->region(), dest_layer);
1737 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1739 r = frozen_playlists.insert (playlist);
1742 playlist->freeze ();
1744 if (rv == _primary) {
1745 rv->region()->set_position (where.frame, last_position.division);
1747 if (rv->region()->position_lock_style() == AudioTime) {
1748 /* move by frame offset */
1749 rv->region()->set_position (where.frame, 0);
1751 /* move by music offset */
1752 rv->region()->set_position_music (rv->region()->quarter_note() - qn_delta);
1755 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1758 if (changed_tracks) {
1760 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1761 was selected in all of them, then removing it from a playlist will have removed all
1762 trace of it from _views (i.e. there were N regions selected, we removed 1,
1763 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1764 corresponding regionview, and _views is now empty).
1766 This could have invalidated any and all iterators into _views.
1768 The heuristic we use here is: if the region selection is empty, break out of the loop
1769 here. if the region selection is not empty, then restart the loop because we know that
1770 we must have removed at least the region(view) we've just been working on as well as any
1771 that we processed on previous iterations.
1773 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1774 we can just iterate.
1778 if (_views.empty()) {
1789 /* If we've created new regions either by copying or moving
1790 to a new track, we want to replace the old selection with the new ones
1793 if (new_views.size() > 0) {
1794 _editor->selection->set (new_views);
1797 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1801 /* write commands for the accumulated diffs for all our modified playlists */
1802 add_stateful_diff_commands_for_playlists (modified_playlists);
1803 /* applies to _brushing */
1804 _editor->commit_reversible_command ();
1806 /* We have futzed with the layering of canvas items on our streamviews.
1807 If any region changed layer, this will have resulted in the stream
1808 views being asked to set up their region views, and all will be well.
1809 If not, we might now have badly-ordered region views. Ask the StreamViews
1810 involved to sort themselves out, just in case.
1813 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1814 (*i)->view()->playlist_layered ((*i)->track ());
1818 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1819 * @param region Region to remove.
1820 * @param playlist playlist To remove from.
1821 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1822 * that clear_changes () is only called once per playlist.
1825 RegionMoveDrag::remove_region_from_playlist (
1826 boost::shared_ptr<Region> region,
1827 boost::shared_ptr<Playlist> playlist,
1828 PlaylistSet& modified_playlists
1831 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1834 playlist->clear_changes ();
1837 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1841 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1842 * clearing the playlist's diff history first if necessary.
1843 * @param region Region to insert.
1844 * @param dest_rtv Destination RouteTimeAxisView.
1845 * @param dest_layer Destination layer.
1846 * @param where Destination position.
1847 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1848 * that clear_changes () is only called once per playlist.
1849 * @return New RegionView, or 0 if no insert was performed.
1852 RegionMoveDrag::insert_region_into_playlist (
1853 boost::shared_ptr<Region> region,
1854 RouteTimeAxisView* dest_rtv,
1857 double quarter_note,
1858 PlaylistSet& modified_playlists,
1862 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1863 if (!dest_playlist) {
1867 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1868 _new_region_view = 0;
1869 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1871 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1872 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1874 dest_playlist->clear_changes ();
1877 dest_playlist->add_region (region, where.frame, 1.0, false, where.division, quarter_note, true);
1879 dest_playlist->add_region (region, where.frame, 1.0, false, where.division);
1882 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1883 dest_playlist->set_layer (region, dest_layer);
1888 assert (_new_region_view);
1890 return _new_region_view;
1894 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1896 _new_region_view = rv;
1900 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1902 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1903 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1905 _editor->session()->add_command (c);
1914 RegionMoveDrag::aborted (bool movement_occurred)
1918 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1919 list<DraggingView>::const_iterator next = i;
1928 RegionMotionDrag::aborted (movement_occurred);
1933 RegionMotionDrag::aborted (bool)
1935 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1937 StreamView* sview = (*i)->view();
1940 if (sview->layer_display() == Expanded) {
1941 sview->set_layer_display (Stacked);
1946 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1947 RegionView* rv = i->view;
1948 TimeAxisView* tv = &(rv->get_time_axis_view ());
1949 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1951 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1952 rv->get_canvas_group()->set_y_position (0);
1954 rv->move (-_total_x_delta, 0);
1955 rv->set_height (rtv->view()->child_height ());
1959 /** @param b true to brush, otherwise false.
1960 * @param c true to make copies of the regions being moved, otherwise false.
1962 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1963 : RegionMotionDrag (e, i, p, v, b)
1965 , _new_region_view (0)
1967 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1970 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1971 if (rtv && rtv->is_track()) {
1972 speed = rtv->track()->speed ();
1975 _last_position = MusicFrame (static_cast<framepos_t> (_primary->region()->position() / speed), 0);
1979 RegionMoveDrag::setup_pointer_frame_offset ()
1981 _pointer_frame_offset = raw_grab_frame() - _last_position.frame;
1984 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1985 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1987 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1989 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1990 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1992 _primary = v->view()->create_region_view (r, false, false);
1994 _primary->get_canvas_group()->show ();
1995 _primary->set_position (pos, 0);
1996 _views.push_back (DraggingView (_primary, this, v));
1998 _last_position = MusicFrame (pos, 0);
2000 _item = _primary->get_canvas_group ();
2004 RegionInsertDrag::finished (GdkEvent * event, bool)
2006 int pos = _views.front().time_axis_view;
2007 assert(pos >= 0 && pos < (int)_time_axis_views.size());
2009 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
2011 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
2012 _primary->get_canvas_group()->set_y_position (0);
2014 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
2016 _editor->begin_reversible_command (Operations::insert_region);
2017 playlist->clear_changes ();
2018 _editor->snap_to_with_modifier (_last_position, event);
2020 playlist->add_region (_primary->region (), _last_position.frame, 1.0, false, _last_position.division);
2022 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
2023 if (Config->get_edit_mode() == Ripple) {
2024 playlist->ripple (_last_position.frame, _primary->region()->length(), _primary->region());
2027 _editor->session()->add_command (new StatefulDiffCommand (playlist));
2028 _editor->commit_reversible_command ();
2036 RegionInsertDrag::aborted (bool)
2043 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2044 : RegionMoveDrag (e, i, p, v, false, false)
2046 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
2049 struct RegionSelectionByPosition {
2050 bool operator() (RegionView*a, RegionView* b) {
2051 return a->region()->position () < b->region()->position();
2056 RegionSpliceDrag::motion (GdkEvent* event, bool)
2058 /* Which trackview is this ? */
2060 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2061 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2063 /* The region motion is only processed if the pointer is over
2067 if (!tv || !tv->is_track()) {
2068 /* To make sure we hide the verbose canvas cursor when the mouse is
2069 not held over an audio track.
2071 _editor->verbose_cursor()->hide ();
2074 _editor->verbose_cursor()->show ();
2079 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
2085 RegionSelection copy;
2086 _editor->selection->regions.by_position(copy);
2088 framepos_t const pf = adjusted_current_frame (event);
2090 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2092 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
2098 boost::shared_ptr<Playlist> playlist;
2100 if ((playlist = atv->playlist()) == 0) {
2104 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
2109 if (pf < (*i)->region()->last_frame() + 1) {
2113 if (pf > (*i)->region()->first_frame()) {
2119 playlist->shuffle ((*i)->region(), dir);
2124 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2126 RegionMoveDrag::finished (event, movement_occurred);
2130 RegionSpliceDrag::aborted (bool)
2140 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2143 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2145 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2146 RegionSelection to_ripple;
2147 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2148 if ((*i)->position() >= where) {
2149 to_ripple.push_back (rtv->view()->find_view(*i));
2153 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2154 if (!exclude.contains (*i)) {
2155 // the selection has already been added to _views
2157 if (drag_in_progress) {
2158 // do the same things that RegionMotionDrag::motion does when
2159 // first_move is true, for the region views that we're adding
2160 // to _views this time
2163 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2164 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2165 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2166 rvg->reparent (_editor->_drag_motion_group);
2168 // we only need to move in the y direction
2169 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2174 _views.push_back (DraggingView (*i, this, tav));
2180 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2183 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2184 // we added all the regions after the selection
2186 std::list<DraggingView>::iterator to_erase = i++;
2187 if (!_editor->selection->regions.contains (to_erase->view)) {
2188 // restore the non-selected regions to their original playlist & positions,
2189 // and then ripple them back by the length of the regions that were dragged away
2190 // do the same things as RegionMotionDrag::aborted
2192 RegionView *rv = to_erase->view;
2193 TimeAxisView* tv = &(rv->get_time_axis_view ());
2194 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2197 // plonk them back onto their own track
2198 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2199 rv->get_canvas_group()->set_y_position (0);
2203 // move the underlying region to match the view
2204 rv->region()->set_position (rv->region()->position() + amount);
2206 // restore the view to match the underlying region's original position
2207 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2210 rv->set_height (rtv->view()->child_height ());
2211 _views.erase (to_erase);
2217 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2219 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2221 return allow_moves_across_tracks;
2229 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2230 : RegionMoveDrag (e, i, p, v, false, false)
2232 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2233 // compute length of selection
2234 RegionSelection selected_regions = _editor->selection->regions;
2235 selection_length = selected_regions.end_frame() - selected_regions.start();
2237 // we'll only allow dragging to another track in ripple mode if all the regions
2238 // being dragged start off on the same track
2239 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2242 exclude = new RegionList;
2243 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2244 exclude->push_back((*i)->region());
2247 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2248 RegionSelection copy;
2249 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2251 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2252 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2254 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2255 // find ripple start point on each applicable playlist
2256 RegionView *first_selected_on_this_track = NULL;
2257 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2258 if ((*i)->region()->playlist() == (*pi)) {
2259 // region is on this playlist - it's the first, because they're sorted
2260 first_selected_on_this_track = *i;
2264 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2265 add_all_after_to_views (
2266 &first_selected_on_this_track->get_time_axis_view(),
2267 first_selected_on_this_track->region()->position(),
2268 selected_regions, false);
2271 if (allow_moves_across_tracks) {
2272 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2280 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2282 /* Which trackview is this ? */
2284 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2285 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2287 /* The region motion is only processed if the pointer is over
2291 if (!tv || !tv->is_track()) {
2292 /* To make sure we hide the verbose canvas cursor when the mouse is
2293 not held over an audiotrack.
2295 _editor->verbose_cursor()->hide ();
2299 framepos_t where = adjusted_current_frame (event);
2300 assert (where >= 0);
2301 MusicFrame after (0, 0);
2302 double delta = compute_x_delta (event, &after);
2304 framecnt_t amount = _editor->pixel_to_sample (delta);
2306 if (allow_moves_across_tracks) {
2307 // all the originally selected regions were on the same track
2309 framecnt_t adjust = 0;
2310 if (prev_tav && tv != prev_tav) {
2311 // dragged onto a different track
2312 // remove the unselected regions from _views, restore them to their original positions
2313 // and add the regions after the drop point on the new playlist to _views instead.
2314 // undo the effect of rippling the previous playlist, and include the effect of removing
2315 // the dragged region(s) from this track
2317 remove_unselected_from_views (prev_amount, false);
2318 // ripple previous playlist according to the regions that have been removed onto the new playlist
2319 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2322 // move just the selected regions
2323 RegionMoveDrag::motion(event, first_move);
2325 // ensure that the ripple operation on the new playlist inserts selection_length time
2326 adjust = selection_length;
2327 // ripple the new current playlist
2328 tv->playlist()->ripple (where, amount+adjust, exclude);
2330 // add regions after point where drag entered this track to subsequent ripples
2331 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2334 // motion on same track
2335 RegionMoveDrag::motion(event, first_move);
2339 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2340 prev_position = where;
2342 // selection encompasses multiple tracks - just drag
2343 // cross-track drags are forbidden
2344 RegionMoveDrag::motion(event, first_move);
2347 if (!_x_constrained) {
2348 prev_amount += amount;
2351 _last_position = after;
2355 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2357 if (!movement_occurred) {
2361 if (was_double_click() && !_views.empty()) {
2362 DraggingView dv = _views.front();
2363 _editor->edit_region (dv.view);
2369 _editor->begin_reversible_command(_("Ripple drag"));
2371 // remove the regions being rippled from the dragging view, updating them to
2372 // their new positions
2373 remove_unselected_from_views (prev_amount, true);
2375 if (allow_moves_across_tracks) {
2377 // if regions were dragged across tracks, we've rippled any later
2378 // regions on the track the regions were dragged off, so we need
2379 // to add the original track to the undo record
2380 orig_tav->playlist()->clear_changes();
2381 vector<Command*> cmds;
2382 orig_tav->playlist()->rdiff (cmds);
2383 _editor->session()->add_commands (cmds);
2385 if (prev_tav && prev_tav != orig_tav) {
2386 prev_tav->playlist()->clear_changes();
2387 vector<Command*> cmds;
2388 prev_tav->playlist()->rdiff (cmds);
2389 _editor->session()->add_commands (cmds);
2392 // selection spanned multiple tracks - all will need adding to undo record
2394 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2395 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2397 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2398 (*pi)->clear_changes();
2399 vector<Command*> cmds;
2400 (*pi)->rdiff (cmds);
2401 _editor->session()->add_commands (cmds);
2405 // other modified playlists are added to undo by RegionMoveDrag::finished()
2406 RegionMoveDrag::finished (event, movement_occurred);
2407 _editor->commit_reversible_command();
2411 RegionRippleDrag::aborted (bool movement_occurred)
2413 RegionMoveDrag::aborted (movement_occurred);
2418 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2420 _view (dynamic_cast<MidiTimeAxisView*> (v))
2422 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2428 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2431 _editor->begin_reversible_command (_("create region"));
2432 _region = add_midi_region (_view, false);
2433 _view->playlist()->freeze ();
2436 framepos_t const f = adjusted_current_frame (event);
2437 if (f < grab_frame()) {
2438 _region->set_initial_position (f);
2441 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2442 so that if this region is duplicated, its duplicate starts on
2443 a snap point rather than 1 frame after a snap point. Otherwise things get
2444 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2445 place snapped notes at the start of the region.
2448 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2449 _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2455 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2457 if (!movement_occurred) {
2458 add_midi_region (_view, true);
2460 _view->playlist()->thaw ();
2461 _editor->commit_reversible_command();
2466 RegionCreateDrag::aborted (bool)
2469 _view->playlist()->thaw ();
2475 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2480 , _was_selected (false)
2483 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2487 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2489 Gdk::Cursor* cursor;
2490 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2492 float x_fraction = cnote->mouse_x_fraction ();
2494 if (x_fraction > 0.0 && x_fraction < 0.25) {
2495 cursor = _editor->cursors()->left_side_trim;
2498 cursor = _editor->cursors()->right_side_trim;
2502 Drag::start_grab (event, cursor);
2504 region = &cnote->region_view();
2507 temp = region->snap_to_pixel (cnote->x0 (), true);
2508 _snap_delta = temp - cnote->x0 ();
2512 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2517 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2518 if (ms.size() > 1) {
2519 /* has to be relative, may make no sense otherwise */
2523 if (!(_was_selected = cnote->selected())) {
2525 /* tertiary-click means extend selection - we'll do that on button release,
2526 so don't add it here, because otherwise we make it hard to figure
2527 out the "extend-to" range.
2530 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2533 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2536 region->note_selected (cnote, true);
2538 _editor->get_selection().clear_points();
2539 region->unique_select (cnote);
2546 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2548 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2550 _editor->begin_reversible_command (_("resize notes"));
2552 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2553 MidiRegionSelection::iterator next;
2556 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2558 mrv->begin_resizing (at_front);
2564 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2565 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2567 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2571 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2573 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2574 if (_editor->snap_mode () != SnapOff) {
2578 if (_editor->snap_mode () == SnapOff) {
2580 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2581 if (apply_snap_delta) {
2587 if (apply_snap_delta) {
2591 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2597 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2599 if (!movement_occurred) {
2600 /* no motion - select note */
2601 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2602 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2603 _editor->current_mouse_mode() == Editing::MouseDraw) {
2605 bool changed = false;
2607 if (_was_selected) {
2608 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2610 region->note_deselected (cnote);
2613 _editor->get_selection().clear_points();
2614 region->unique_select (cnote);
2618 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2619 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2621 if (!extend && !add && region->selection_size() > 1) {
2622 _editor->get_selection().clear_points();
2623 region->unique_select (cnote);
2625 } else if (extend) {
2626 region->note_selected (cnote, true, true);
2629 /* it was added during button press */
2635 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2636 _editor->commit_reversible_selection_op();
2643 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2644 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2645 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2647 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2650 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2652 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2653 if (_editor->snap_mode () != SnapOff) {
2657 if (_editor->snap_mode () == SnapOff) {
2659 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2660 if (apply_snap_delta) {
2666 if (apply_snap_delta) {
2670 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2674 _editor->commit_reversible_command ();
2678 NoteResizeDrag::aborted (bool)
2680 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2681 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2682 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2684 mrv->abort_resizing ();
2689 AVDraggingView::AVDraggingView (RegionView* v)
2692 initial_position = v->region()->position ();
2695 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2698 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2701 TrackViewList empty;
2703 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2704 std::list<RegionView*> views = rs.by_layer();
2707 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2708 RegionView* rv = (*i);
2709 if (!rv->region()->video_locked()) {
2712 if (rv->region()->locked()) {
2715 _views.push_back (AVDraggingView (rv));
2720 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2722 Drag::start_grab (event);
2723 if (_editor->session() == 0) {
2727 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2733 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2737 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2738 _max_backwards_drag = (
2739 ARDOUR_UI::instance()->video_timeline->get_duration()
2740 + ARDOUR_UI::instance()->video_timeline->get_offset()
2741 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2744 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2745 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2746 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2749 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2752 Timecode::Time timecode;
2753 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2754 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);
2755 show_verbose_cursor_text (buf);
2759 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2761 if (_editor->session() == 0) {
2764 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2768 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2772 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2773 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2775 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2776 dt = - _max_backwards_drag;
2779 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2780 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2782 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2783 RegionView* rv = i->view;
2784 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2787 rv->region()->clear_changes ();
2788 rv->region()->suspend_property_changes();
2790 rv->region()->set_position(i->initial_position + dt);
2791 rv->region_changed(ARDOUR::Properties::position);
2794 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2795 Timecode::Time timecode;
2796 Timecode::Time timediff;
2798 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2799 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2800 snprintf (buf, sizeof (buf),
2801 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2802 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2803 , _("Video Start:"),
2804 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2806 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2808 show_verbose_cursor_text (buf);
2812 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2814 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2821 if (!movement_occurred || ! _editor->session()) {
2825 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2827 _editor->begin_reversible_command (_("Move Video"));
2829 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2830 ARDOUR_UI::instance()->video_timeline->save_undo();
2831 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2832 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2834 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2835 i->view->drag_end();
2836 i->view->region()->resume_property_changes ();
2838 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2841 _editor->session()->maybe_update_session_range(
2842 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2843 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2847 _editor->commit_reversible_command ();
2851 VideoTimeLineDrag::aborted (bool)
2853 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2856 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2857 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2859 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2860 i->view->region()->resume_property_changes ();
2861 i->view->region()->set_position(i->initial_position);
2865 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2866 : RegionDrag (e, i, p, v)
2867 , _operation (StartTrim)
2868 , _preserve_fade_anchor (preserve_fade_anchor)
2869 , _jump_position_when_done (false)
2871 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2875 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2878 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2879 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2881 if (tv && tv->is_track()) {
2882 speed = tv->track()->speed();
2885 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2886 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2887 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2889 framepos_t const pf = adjusted_current_frame (event);
2890 setup_snap_delta (MusicFrame(region_start, 0));
2892 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2893 /* Move the contents of the region around without changing the region bounds */
2894 _operation = ContentsTrim;
2895 Drag::start_grab (event, _editor->cursors()->trimmer);
2897 /* These will get overridden for a point trim.*/
2898 if (pf < (region_start + region_length/2)) {
2899 /* closer to front */
2900 _operation = StartTrim;
2901 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2902 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2904 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2908 _operation = EndTrim;
2909 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2910 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2912 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2916 /* jump trim disabled for now
2917 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2918 _jump_position_when_done = true;
2922 switch (_operation) {
2924 show_verbose_cursor_time (region_start);
2927 show_verbose_cursor_duration (region_start, region_end);
2930 show_verbose_cursor_time (pf);
2934 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2935 i->view->region()->suspend_property_changes ();
2940 TrimDrag::motion (GdkEvent* event, bool first_move)
2942 RegionView* rv = _primary;
2945 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2946 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2947 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2948 frameoffset_t frame_delta = 0;
2950 if (tv && tv->is_track()) {
2951 speed = tv->track()->speed();
2953 MusicFrame adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2954 framecnt_t dt = adj_frame.frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2960 switch (_operation) {
2962 trim_type = "Region start trim";
2965 trim_type = "Region end trim";
2968 trim_type = "Region content trim";
2975 _editor->begin_reversible_command (trim_type);
2977 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2978 RegionView* rv = i->view;
2979 rv->region()->playlist()->clear_owned_changes ();
2981 if (_operation == StartTrim) {
2982 rv->trim_front_starting ();
2985 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2988 arv->temporarily_hide_envelope ();
2992 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2993 insert_result = _editor->motion_frozen_playlists.insert (pl);
2995 if (insert_result.second) {
2999 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (rv);
3000 /* a MRV start trim may change the source length. ensure we cover all playlists here */
3001 if (mrv && _operation == StartTrim) {
3002 vector<boost::shared_ptr<Playlist> > all_playlists;
3003 _editor->session()->playlists->get (all_playlists);
3004 for (vector<boost::shared_ptr<Playlist> >::iterator x = all_playlists.begin(); x != all_playlists.end(); ++x) {
3006 if ((*x)->uses_source (rv->region()->source(0))) {
3007 insert_result = _editor->motion_frozen_playlists.insert (*x);
3008 if (insert_result.second) {
3009 (*x)->clear_owned_changes ();
3019 bool non_overlap_trim = false;
3021 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
3022 non_overlap_trim = true;
3025 /* contstrain trim to fade length */
3026 if (_preserve_fade_anchor) {
3027 switch (_operation) {
3029 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3030 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3032 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3033 if (ar->locked()) continue;
3034 framecnt_t len = ar->fade_in()->back()->when;
3035 if (len < dt) dt = min(dt, len);
3039 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3040 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3042 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3043 if (ar->locked()) continue;
3044 framecnt_t len = ar->fade_out()->back()->when;
3045 if (len < -dt) dt = max(dt, -len);
3053 switch (_operation) {
3055 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3056 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
3057 , adj_frame.division);
3059 if (changed && _preserve_fade_anchor) {
3060 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3062 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3063 framecnt_t len = ar->fade_in()->back()->when;
3064 framecnt_t diff = ar->first_frame() - i->initial_position;
3065 framepos_t new_length = len - diff;
3066 i->anchored_fade_length = min (ar->length(), new_length);
3067 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
3068 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
3075 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3076 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, adj_frame.division);
3077 if (changed && _preserve_fade_anchor) {
3078 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3080 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3081 framecnt_t len = ar->fade_out()->back()->when;
3082 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
3083 framepos_t new_length = len + diff;
3084 i->anchored_fade_length = min (ar->length(), new_length);
3085 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
3086 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
3094 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
3096 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3097 i->view->move_contents (frame_delta);
3103 switch (_operation) {
3105 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
3108 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
3111 // show_verbose_cursor_time (frame_delta);
3117 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
3119 if (movement_occurred) {
3120 motion (event, false);
3122 if (_operation == StartTrim) {
3123 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3125 /* This must happen before the region's StatefulDiffCommand is created, as it may
3126 `correct' (ahem) the region's _start from being negative to being zero. It
3127 needs to be zero in the undo record.
3129 i->view->trim_front_ending ();
3131 if (_preserve_fade_anchor) {
3132 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3134 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3135 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3136 ar->set_fade_in_length(i->anchored_fade_length);
3137 ar->set_fade_in_active(true);
3140 if (_jump_position_when_done) {
3141 i->view->region()->set_position (i->initial_position);
3144 } else if (_operation == EndTrim) {
3145 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3146 if (_preserve_fade_anchor) {
3147 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3149 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3150 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3151 ar->set_fade_out_length(i->anchored_fade_length);
3152 ar->set_fade_out_active(true);
3155 if (_jump_position_when_done) {
3156 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3161 if (!_editor->selection->selected (_primary)) {
3162 _primary->thaw_after_trim ();
3164 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3165 i->view->thaw_after_trim ();
3166 i->view->enable_display (true);
3170 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3171 /* Trimming one region may affect others on the playlist, so we need
3172 to get undo Commands from the whole playlist rather than just the
3173 region. Use motion_frozen_playlists (a set) to make sure we don't
3174 diff a given playlist more than once.
3177 vector<Command*> cmds;
3179 _editor->session()->add_commands (cmds);
3183 _editor->motion_frozen_playlists.clear ();
3184 _editor->commit_reversible_command();
3187 /* no mouse movement */
3188 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false).frame) {
3189 _editor->point_trim (event, adjusted_current_frame (event));
3193 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3194 i->view->region()->resume_property_changes ();
3199 TrimDrag::aborted (bool movement_occurred)
3201 /* Our motion method is changing model state, so use the Undo system
3202 to cancel. Perhaps not ideal, as this will leave an Undo point
3203 behind which may be slightly odd from the user's point of view.
3207 finished (&ev, true);
3209 if (movement_occurred) {
3210 _editor->session()->undo (1);
3213 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3214 i->view->region()->resume_property_changes ();
3219 TrimDrag::setup_pointer_frame_offset ()
3221 list<DraggingView>::iterator i = _views.begin ();
3222 while (i != _views.end() && i->view != _primary) {
3226 if (i == _views.end()) {
3230 switch (_operation) {
3232 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3235 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3242 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3245 , _old_snap_type (e->snap_type())
3246 , _old_snap_mode (e->snap_mode())
3249 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3250 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3252 _real_section = &_marker->meter();
3257 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3259 Drag::start_grab (event, cursor);
3260 show_verbose_cursor_time (adjusted_current_frame(event));
3264 MeterMarkerDrag::setup_pointer_frame_offset ()
3266 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3270 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3273 // create a dummy marker to catch events, then hide it.
3276 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3278 _marker = new MeterMarker (
3280 *_editor->meter_group,
3281 UIConfiguration::instance().color ("meter marker"),
3283 *new MeterSection (_marker->meter())
3286 /* use the new marker for the grab */
3287 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3290 TempoMap& map (_editor->session()->tempo_map());
3291 /* get current state */
3292 before_state = &map.get_state();
3295 _editor->begin_reversible_command (_("move meter mark"));
3297 _editor->begin_reversible_command (_("copy meter mark"));
3299 Timecode::BBT_Time bbt = _real_section->bbt();
3301 /* we can't add a meter where one currently exists */
3302 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3307 const double beat = map.beat_at_bbt (bbt);
3308 const framepos_t frame = map.frame_at_beat (beat);
3309 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3310 , beat, bbt, frame, _real_section->position_lock_style());
3311 if (!_real_section) {
3317 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3318 if (_real_section->position_lock_style() != AudioTime) {
3319 _editor->set_snap_to (SnapToBar);
3320 _editor->set_snap_mode (SnapNormal);
3324 framepos_t pf = adjusted_current_frame (event);
3326 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3327 /* never snap to music for audio locked */
3328 pf = adjusted_current_frame (event, false);
3331 _editor->session()->tempo_map().gui_set_meter_position (_real_section, pf);
3333 /* fake marker meeds to stay under the mouse, unlike the real one. */
3334 _marker->set_position (adjusted_current_frame (event, false));
3336 show_verbose_cursor_time (_real_section->frame());
3340 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3342 if (!movement_occurred) {
3343 if (was_double_click()) {
3344 _editor->edit_meter_marker (*_marker);
3349 /* reinstate old snap setting */
3350 _editor->set_snap_to (_old_snap_type);
3351 _editor->set_snap_mode (_old_snap_mode);
3353 TempoMap& map (_editor->session()->tempo_map());
3355 XMLNode &after = map.get_state();
3356 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3357 _editor->commit_reversible_command ();
3359 // delete the dummy marker we used for visual representation while moving.
3360 // a new visual marker will show up automatically.
3365 MeterMarkerDrag::aborted (bool moved)
3367 _marker->set_position (_marker->meter().frame ());
3369 /* reinstate old snap setting */
3370 _editor->set_snap_to (_old_snap_type);
3371 _editor->set_snap_mode (_old_snap_mode);
3373 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3374 // delete the dummy marker we used for visual representation while moving.
3375 // a new visual marker will show up automatically.
3380 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3383 , _grab_bpm (120.0, 4.0)
3387 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3389 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3390 _real_section = &_marker->tempo();
3391 _movable = !_real_section->initial();
3392 _grab_bpm = Tempo (_real_section->note_types_per_minute(), _real_section->note_type(), _real_section->end_note_types_per_minute());
3393 _grab_qn = _real_section->pulse() * 4.0;
3398 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3400 Drag::start_grab (event, cursor);
3401 if (!_real_section->active()) {
3402 show_verbose_cursor_text (_("inactive"));
3404 show_verbose_cursor_time (adjusted_current_frame (event));
3409 TempoMarkerDrag::setup_pointer_frame_offset ()
3411 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3415 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3417 if (!_real_section->active()) {
3420 TempoMap& map (_editor->session()->tempo_map());
3424 // mvc drag - create a dummy marker to catch events, hide it.
3427 snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute());
3429 TempoSection section (_marker->tempo());
3431 _marker = new TempoMarker (
3433 *_editor->tempo_group,
3434 UIConfiguration::instance().color ("tempo marker"),
3436 *new TempoSection (_marker->tempo())
3439 /* use the new marker for the grab */
3440 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3443 /* get current state */
3444 _before_state = &map.get_state();
3447 _editor->begin_reversible_command (_("move tempo mark"));
3450 const Tempo tempo (_marker->tempo());
3451 const framepos_t frame = adjusted_current_frame (event) + 1;
3453 _editor->begin_reversible_command (_("copy tempo mark"));
3455 if (_real_section->position_lock_style() == MusicTime) {
3456 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
3457 _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, MusicTime);
3459 _real_section = map.add_tempo (tempo, 0.0, frame, AudioTime);
3462 if (!_real_section) {
3469 if (ArdourKeyboard::indicates_constraint (event->button.state) && ArdourKeyboard::indicates_copy (event->button.state)) {
3470 double new_bpm = max (1.5, _grab_bpm.end_note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3472 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (_real_section->note_types_per_minute(), _real_section->note_type(), new_bpm));
3473 strs << "end:" << fixed << setprecision(3) << new_bpm;
3474 show_verbose_cursor_text (strs.str());
3476 } else if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3477 /* use vertical movement to alter tempo .. should be log */
3478 double new_bpm = max (1.5, _grab_bpm.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3480 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type(), _real_section->end_note_types_per_minute()));
3481 strs << "start:" << fixed << setprecision(3) << new_bpm;
3482 show_verbose_cursor_text (strs.str());
3484 } else if (_movable && !_real_section->locked_to_meter()) {
3487 if (_editor->snap_musical()) {
3488 /* we can't snap to a grid that we are about to move.
3489 * gui_move_tempo() will sort out snap using the supplied beat divisions.
3491 pf = adjusted_current_frame (event, false);
3493 pf = adjusted_current_frame (event);
3496 /* snap to beat is 1, snap to bar is -1 (sorry) */
3497 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3499 map.gui_set_tempo_position (_real_section, pf, sub_num);
3501 show_verbose_cursor_time (_real_section->frame());
3503 _marker->set_position (adjusted_current_frame (event, false));
3507 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3509 if (!_real_section->active()) {
3512 if (!movement_occurred) {
3513 if (was_double_click()) {
3514 _editor->edit_tempo_marker (*_marker);
3519 TempoMap& map (_editor->session()->tempo_map());
3521 XMLNode &after = map.get_state();
3522 _editor->session()->add_command (new MementoCommand<TempoMap>(map, _before_state, &after));
3523 _editor->commit_reversible_command ();
3525 // delete the dummy marker we used for visual representation while moving.
3526 // a new visual marker will show up automatically.
3531 TempoMarkerDrag::aborted (bool moved)
3533 _marker->set_position (_marker->tempo().frame());
3535 TempoMap& map (_editor->session()->tempo_map());
3536 map.set_state (*_before_state, Stateful::current_state_version);
3537 // delete the dummy (hidden) marker we used for events while moving.
3542 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3548 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3553 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3555 Drag::start_grab (event, cursor);
3556 TempoMap& map (_editor->session()->tempo_map());
3557 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3558 _editor->tempo_curve_selected (_tempo, true);
3561 if (_tempo->clamped()) {
3562 TempoSection* prev = map.previous_tempo_section (_tempo);
3564 _editor->tempo_curve_selected (prev, true);
3565 sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
3569 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3570 show_verbose_cursor_text (sstr.str());
3574 BBTRulerDrag::setup_pointer_frame_offset ()
3576 TempoMap& map (_editor->session()->tempo_map());
3577 /* get current state */
3578 _before_state = &map.get_state();
3580 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3581 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3584 if (divisions > 0) {
3585 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3587 /* while it makes some sense for the user to determine the division to 'grab',
3588 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3589 and the result over steep tempo curves. Use sixteenths.
3591 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3594 _grab_qn = map.quarter_note_at_beat (beat);
3596 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3601 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3603 TempoMap& map (_editor->session()->tempo_map());
3606 _editor->begin_reversible_command (_("stretch tempo"));
3611 if (_editor->snap_musical()) {
3612 pf = adjusted_current_frame (event, false);
3614 pf = adjusted_current_frame (event);
3617 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3618 /* adjust previous tempo to match pointer frame */
3619 _editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
3623 if (_tempo->clamped()) {
3624 TempoSection* prev = map.previous_tempo_section (_tempo);
3626 _editor->tempo_curve_selected (prev, true);
3627 sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
3630 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3631 show_verbose_cursor_text (sstr.str());
3635 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3637 if (!movement_occurred) {
3641 TempoMap& map (_editor->session()->tempo_map());
3643 XMLNode &after = map.get_state();
3644 _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
3645 _editor->commit_reversible_command ();
3646 _editor->tempo_curve_selected (_tempo, false);
3648 if (_tempo->clamped()) {
3649 TempoSection* prev_tempo = map.previous_tempo_section (_tempo);
3651 _editor->tempo_curve_selected (prev_tempo, false);
3657 BBTRulerDrag::aborted (bool moved)
3660 _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
3664 TempoTwistDrag::TempoTwistDrag (Editor* e, ArdourCanvas::Item* i)
3670 , _drag_valid (true)
3673 DEBUG_TRACE (DEBUG::Drags, "New TempoTwistDrag\n");
3678 TempoTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3680 Drag::start_grab (event, cursor);
3681 TempoMap& map (_editor->session()->tempo_map());
3682 /* get current state */
3683 _before_state = &map.get_state();
3684 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3686 _next_tempo = map.next_tempo_section (_tempo);
3688 if (!map.next_tempo_section (_next_tempo)) {
3689 _drag_valid = false;
3690 finished (event, false);
3694 _editor->tempo_curve_selected (_tempo, true);
3695 _editor->tempo_curve_selected (_next_tempo, true);
3698 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
3699 sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
3700 sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
3701 show_verbose_cursor_text (sstr.str());
3703 _drag_valid = false;
3706 _grab_tempo = Tempo (_tempo->note_types_per_minute(), _tempo->note_type());
3710 TempoTwistDrag::setup_pointer_frame_offset ()
3712 TempoMap& map (_editor->session()->tempo_map());
3713 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3714 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3717 if (divisions > 0) {
3718 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3720 /* while it makes some sense for the user to determine the division to 'grab',
3721 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3722 and the result over steep tempo curves. Use sixteenths.
3724 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3727 _grab_qn = map.quarter_note_at_beat (beat);
3729 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3734 TempoTwistDrag::motion (GdkEvent* event, bool first_move)
3737 if (!_next_tempo || !_drag_valid) {
3741 TempoMap& map (_editor->session()->tempo_map());
3744 _editor->begin_reversible_command (_("twist tempo"));
3749 if (_editor->snap_musical()) {
3750 pf = adjusted_current_frame (event, false);
3752 pf = adjusted_current_frame (event);
3755 /* adjust this and the next tempi to match pointer frame */
3756 double new_bpm = max (1.5, _grab_tempo.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3757 _editor->session()->tempo_map().gui_twist_tempi (_tempo, new_bpm, map.frame_at_quarter_note (_grab_qn), pf);
3760 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
3761 sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
3762 sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
3763 show_verbose_cursor_text (sstr.str());
3767 TempoTwistDrag::finished (GdkEvent* event, bool movement_occurred)
3769 TempoMap& map (_editor->session()->tempo_map());
3771 if (!movement_occurred || !_drag_valid) {
3775 _editor->tempo_curve_selected (_tempo, false);
3776 _editor->tempo_curve_selected (_next_tempo, false);
3778 XMLNode &after = map.get_state();
3779 _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
3780 _editor->commit_reversible_command ();
3784 TempoTwistDrag::aborted (bool moved)
3787 _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
3791 TempoEndDrag::TempoEndDrag (Editor* e, ArdourCanvas::Item* i)
3797 DEBUG_TRACE (DEBUG::Drags, "New TempoEndDrag\n");
3798 TempoMarker* marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3799 _tempo = &marker->tempo();
3800 _grab_qn = _tempo->pulse() * 4.0;
3804 TempoEndDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3806 Drag::start_grab (event, cursor);
3807 TempoMap& tmap (_editor->session()->tempo_map());
3809 /* get current state */
3810 _before_state = &tmap.get_state();
3815 TempoSection* prev = 0;
3816 if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
3817 _editor->tempo_curve_selected (tmap.previous_tempo_section (_tempo), true);
3818 sstr << "end: " << fixed << setprecision(3) << tmap.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
3821 if (_tempo->clamped()) {
3822 _editor->tempo_curve_selected (_tempo, true);
3823 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3826 show_verbose_cursor_text (sstr.str());
3830 TempoEndDrag::setup_pointer_frame_offset ()
3832 TempoMap& map (_editor->session()->tempo_map());
3834 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3839 TempoEndDrag::motion (GdkEvent* event, bool first_move)
3841 TempoMap& map (_editor->session()->tempo_map());
3844 _editor->begin_reversible_command (_("stretch end tempo"));
3849 framepos_t const pf = adjusted_current_frame (event, false);
3850 map.gui_stretch_tempo_end (&map.tempo_section_at_frame (_tempo->frame() - 1), map.frame_at_quarter_note (_grab_qn), pf);
3853 sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
3855 if (_tempo->clamped()) {
3856 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3859 show_verbose_cursor_text (sstr.str());
3863 TempoEndDrag::finished (GdkEvent* event, bool movement_occurred)
3865 if (!movement_occurred) {
3869 TempoMap& tmap (_editor->session()->tempo_map());
3871 XMLNode &after = tmap.get_state();
3872 _editor->session()->add_command(new MementoCommand<TempoMap>(tmap, _before_state, &after));
3873 _editor->commit_reversible_command ();
3875 TempoSection* prev = 0;
3876 if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
3877 _editor->tempo_curve_selected (prev, false);
3880 if (_tempo->clamped()) {
3881 _editor->tempo_curve_selected (_tempo, false);
3887 TempoEndDrag::aborted (bool moved)
3890 _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
3894 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3895 : Drag (e, &c.track_canvas_item(), false)
3900 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3903 /** Do all the things we do when dragging the playhead to make it look as though
3904 * we have located, without actually doing the locate (because that would cause
3905 * the diskstream buffers to be refilled, which is too slow).
3908 CursorDrag::fake_locate (framepos_t t)
3910 if (_editor->session () == 0) {
3914 _editor->playhead_cursor->set_position (t);
3916 Session* s = _editor->session ();
3917 if (s->timecode_transmission_suspended ()) {
3918 framepos_t const f = _editor->playhead_cursor->current_frame ();
3919 /* This is asynchronous so it will be sent "now"
3921 s->send_mmc_locate (f);
3922 /* These are synchronous and will be sent during the next
3925 s->queue_full_time_code ();
3926 s->queue_song_position_pointer ();
3929 show_verbose_cursor_time (t);
3930 _editor->UpdateAllTransportClocks (t);
3934 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3936 Drag::start_grab (event, c);
3937 setup_snap_delta (MusicFrame (_editor->playhead_cursor->current_frame(), 0));
3939 _grab_zoom = _editor->samples_per_pixel;
3941 MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3943 _editor->snap_to_with_modifier (where, event);
3944 _editor->_dragging_playhead = true;
3945 _editor->_control_scroll_target = where.frame;
3947 Session* s = _editor->session ();
3949 /* grab the track canvas item as well */
3951 _cursor.track_canvas_item().grab();
3954 if (_was_rolling && _stop) {
3958 if (s->is_auditioning()) {
3959 s->cancel_audition ();
3963 if (AudioEngine::instance()->connected()) {
3965 /* do this only if we're the engine is connected
3966 * because otherwise this request will never be
3967 * serviced and we'll busy wait forever. likewise,
3968 * notice if we are disconnected while waiting for the
3969 * request to be serviced.
3972 s->request_suspend_timecode_transmission ();
3973 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3974 /* twiddle our thumbs */
3979 fake_locate (where.frame - snap_delta (event->button.state));
3983 CursorDrag::motion (GdkEvent* event, bool)
3985 MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3987 _editor->snap_to_with_modifier (where, event);
3989 if (where.frame != last_pointer_frame()) {
3990 fake_locate (where.frame - snap_delta (event->button.state));
3995 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3997 _editor->_dragging_playhead = false;
3999 _cursor.track_canvas_item().ungrab();
4001 if (!movement_occurred && _stop) {
4005 motion (event, false);
4007 Session* s = _editor->session ();
4009 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
4010 _editor->_pending_locate_request = true;
4011 s->request_resume_timecode_transmission ();
4016 CursorDrag::aborted (bool)
4018 _cursor.track_canvas_item().ungrab();
4020 if (_editor->_dragging_playhead) {
4021 _editor->session()->request_resume_timecode_transmission ();
4022 _editor->_dragging_playhead = false;
4025 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false).frame);
4028 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
4029 : RegionDrag (e, i, p, v)
4031 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
4035 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4037 Drag::start_grab (event, cursor);
4039 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4040 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
4041 setup_snap_delta (MusicFrame (r->position(), 0));
4043 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
4047 FadeInDrag::setup_pointer_frame_offset ()
4049 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4050 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
4051 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
4055 FadeInDrag::motion (GdkEvent* event, bool)
4057 framecnt_t fade_length;
4059 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4060 _editor->snap_to_with_modifier (pos, event);
4062 pos.frame -= snap_delta (event->button.state);
4064 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4066 if (pos.frame < (region->position() + 64)) {
4067 fade_length = 64; // this should be a minimum defined somewhere
4068 } else if (pos.frame > region->position() + region->length() - region->fade_out()->back()->when) {
4069 fade_length = region->length() - region->fade_out()->back()->when - 1;
4071 fade_length = pos.frame - region->position();
4074 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4076 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4082 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
4085 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
4089 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
4091 if (!movement_occurred) {
4095 framecnt_t fade_length;
4096 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4098 _editor->snap_to_with_modifier (pos, event);
4099 pos.frame -= snap_delta (event->button.state);
4101 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4103 if (pos.frame < (region->position() + 64)) {
4104 fade_length = 64; // this should be a minimum defined somewhere
4105 } else if (pos.frame >= region->position() + region->length() - region->fade_out()->back()->when) {
4106 fade_length = region->length() - region->fade_out()->back()->when - 1;
4108 fade_length = pos.frame - region->position();
4111 bool in_command = false;
4113 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4115 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4121 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
4122 XMLNode &before = alist->get_state();
4124 tmp->audio_region()->set_fade_in_length (fade_length);
4125 tmp->audio_region()->set_fade_in_active (true);
4128 _editor->begin_reversible_command (_("change fade in length"));
4131 XMLNode &after = alist->get_state();
4132 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4136 _editor->commit_reversible_command ();
4141 FadeInDrag::aborted (bool)
4143 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4144 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4150 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
4154 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
4155 : RegionDrag (e, i, p, v)
4157 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
4161 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4163 Drag::start_grab (event, cursor);
4165 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4166 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
4167 setup_snap_delta (MusicFrame (r->last_frame(), 0));
4169 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
4173 FadeOutDrag::setup_pointer_frame_offset ()
4175 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4176 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
4177 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
4181 FadeOutDrag::motion (GdkEvent* event, bool)
4183 framecnt_t fade_length;
4184 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4186 _editor->snap_to_with_modifier (pos, event);
4187 pos.frame -= snap_delta (event->button.state);
4189 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4191 if (pos.frame > (region->last_frame() - 64)) {
4192 fade_length = 64; // this should really be a minimum fade defined somewhere
4193 } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
4194 fade_length = region->length() - region->fade_in()->back()->when - 1;
4196 fade_length = region->last_frame() - pos.frame;
4199 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4201 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4207 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
4210 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
4214 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
4216 if (!movement_occurred) {
4220 framecnt_t fade_length;
4221 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4223 _editor->snap_to_with_modifier (pos, event);
4224 pos.frame -= snap_delta (event->button.state);
4226 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4228 if (pos.frame > (region->last_frame() - 64)) {
4229 fade_length = 64; // this should really be a minimum fade defined somewhere
4230 } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
4231 fade_length = region->length() - region->fade_in()->back()->when - 1;
4233 fade_length = region->last_frame() - pos.frame;
4236 bool in_command = false;
4238 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4240 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4246 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
4247 XMLNode &before = alist->get_state();
4249 tmp->audio_region()->set_fade_out_length (fade_length);
4250 tmp->audio_region()->set_fade_out_active (true);
4253 _editor->begin_reversible_command (_("change fade out length"));
4256 XMLNode &after = alist->get_state();
4257 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4261 _editor->commit_reversible_command ();
4266 FadeOutDrag::aborted (bool)
4268 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4269 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4275 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
4279 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
4281 , _selection_changed (false)
4283 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
4284 Gtk::Window* toplevel = _editor->current_toplevel();
4285 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
4289 _points.push_back (ArdourCanvas::Duple (0, 0));
4291 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
4294 MarkerDrag::~MarkerDrag ()
4296 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
4301 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
4303 location = new Location (*l);
4304 markers.push_back (m);
4309 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4311 Drag::start_grab (event, cursor);
4315 Location *location = _editor->find_location_from_marker (_marker, is_start);
4316 _editor->_dragging_edit_point = true;
4318 update_item (location);
4320 // _drag_line->show();
4321 // _line->raise_to_top();
4324 show_verbose_cursor_time (location->start());
4326 show_verbose_cursor_time (location->end());
4328 setup_snap_delta (MusicFrame (is_start ? location->start() : location->end(), 0));
4330 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4333 case Selection::Toggle:
4334 /* we toggle on the button release */
4336 case Selection::Set:
4337 if (!_editor->selection->selected (_marker)) {
4338 _editor->selection->set (_marker);
4339 _selection_changed = true;
4342 case Selection::Extend:
4344 Locations::LocationList ll;
4345 list<ArdourMarker*> to_add;
4347 _editor->selection->markers.range (s, e);
4348 s = min (_marker->position(), s);
4349 e = max (_marker->position(), e);
4352 if (e < max_framepos) {
4355 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
4356 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
4357 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
4360 to_add.push_back (lm->start);
4363 to_add.push_back (lm->end);
4367 if (!to_add.empty()) {
4368 _editor->selection->add (to_add);
4369 _selection_changed = true;
4373 case Selection::Add:
4374 _editor->selection->add (_marker);
4375 _selection_changed = true;
4380 /* Set up copies for us to manipulate during the drag
4383 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4385 Location* l = _editor->find_location_from_marker (*i, is_start);
4392 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4394 /* range: check that the other end of the range isn't
4397 CopiedLocationInfo::iterator x;
4398 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4399 if (*(*x).location == *l) {
4403 if (x == _copied_locations.end()) {
4404 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4406 (*x).markers.push_back (*i);
4407 (*x).move_both = true;
4415 MarkerDrag::setup_pointer_frame_offset ()
4418 Location *location = _editor->find_location_from_marker (_marker, is_start);
4419 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4423 MarkerDrag::motion (GdkEvent* event, bool)
4425 framecnt_t f_delta = 0;
4427 bool move_both = false;
4428 Location *real_location;
4429 Location *copy_location = 0;
4430 framecnt_t const sd = snap_delta (event->button.state);
4432 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true).frame - sd;
4433 framepos_t next = newframe;
4435 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4439 CopiedLocationInfo::iterator x;
4441 /* find the marker we're dragging, and compute the delta */
4443 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4445 copy_location = (*x).location;
4447 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4449 /* this marker is represented by this
4450 * CopiedLocationMarkerInfo
4453 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4458 if (real_location->is_mark()) {
4459 f_delta = newframe - copy_location->start();
4463 switch (_marker->type()) {
4464 case ArdourMarker::SessionStart:
4465 case ArdourMarker::RangeStart:
4466 case ArdourMarker::LoopStart:
4467 case ArdourMarker::PunchIn:
4468 f_delta = newframe - copy_location->start();
4471 case ArdourMarker::SessionEnd:
4472 case ArdourMarker::RangeEnd:
4473 case ArdourMarker::LoopEnd:
4474 case ArdourMarker::PunchOut:
4475 f_delta = newframe - copy_location->end();
4478 /* what kind of marker is this ? */
4487 if (x == _copied_locations.end()) {
4488 /* hmm, impossible - we didn't find the dragged marker */
4492 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4494 /* now move them all */
4496 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4498 copy_location = x->location;
4500 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4504 if (real_location->locked()) {
4508 if (copy_location->is_mark()) {
4511 copy_location->set_start (copy_location->start() + f_delta, false, true, divisions);
4515 framepos_t new_start = copy_location->start() + f_delta;
4516 framepos_t new_end = copy_location->end() + f_delta;
4518 if (is_start) { // start-of-range marker
4520 if (move_both || (*x).move_both) {
4521 copy_location->set_start (new_start, false, true, divisions);
4522 copy_location->set_end (new_end, false, true, divisions);
4523 } else if (new_start < copy_location->end()) {
4524 copy_location->set_start (new_start, false, true, divisions);
4525 } else if (newframe > 0) {
4526 //_editor->snap_to (next, RoundUpAlways, true);
4527 copy_location->set_end (next, false, true, divisions);
4528 copy_location->set_start (newframe, false, true, divisions);
4531 } else { // end marker
4533 if (move_both || (*x).move_both) {
4534 copy_location->set_end (new_end, divisions);
4535 copy_location->set_start (new_start, false, true, divisions);
4536 } else if (new_end > copy_location->start()) {
4537 copy_location->set_end (new_end, false, true, divisions);
4538 } else if (newframe > 0) {
4539 //_editor->snap_to (next, RoundDownAlways, true);
4540 copy_location->set_start (next, false, true, divisions);
4541 copy_location->set_end (newframe, false, true, divisions);
4546 update_item (copy_location);
4548 /* now lookup the actual GUI items used to display this
4549 * location and move them to wherever the copy of the location
4550 * is now. This means that the logic in ARDOUR::Location is
4551 * still enforced, even though we are not (yet) modifying
4552 * the real Location itself.
4555 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4558 lm->set_position (copy_location->start(), copy_location->end());
4563 assert (!_copied_locations.empty());
4565 show_verbose_cursor_time (newframe);
4569 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4571 if (!movement_occurred) {
4573 if (was_double_click()) {
4574 _editor->rename_marker (_marker);
4578 /* just a click, do nothing but finish
4579 off the selection process
4582 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4584 case Selection::Set:
4585 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4586 _editor->selection->set (_marker);
4587 _selection_changed = true;
4591 case Selection::Toggle:
4592 /* we toggle on the button release, click only */
4593 _editor->selection->toggle (_marker);
4594 _selection_changed = true;
4598 case Selection::Extend:
4599 case Selection::Add:
4603 if (_selection_changed) {
4604 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4605 _editor->commit_reversible_selection_op();
4611 _editor->_dragging_edit_point = false;
4613 XMLNode &before = _editor->session()->locations()->get_state();
4614 bool in_command = false;
4616 MarkerSelection::iterator i;
4617 CopiedLocationInfo::iterator x;
4618 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4621 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4622 x != _copied_locations.end() && i != _editor->selection->markers.end();
4625 Location * location = _editor->find_location_from_marker (*i, is_start);
4629 if (location->locked()) {
4633 _editor->begin_reversible_command ( _("move marker") );
4636 if (location->is_mark()) {
4637 location->set_start (((*x).location)->start(), false, true, divisions);
4639 location->set (((*x).location)->start(), ((*x).location)->end(), true, divisions);
4642 if (location->is_session_range()) {
4643 _editor->session()->set_end_is_free (false);
4649 XMLNode &after = _editor->session()->locations()->get_state();
4650 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4651 _editor->commit_reversible_command ();
4656 MarkerDrag::aborted (bool movement_occurred)
4658 if (!movement_occurred) {
4662 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4664 /* move all markers to their original location */
4667 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4670 Location * location = _editor->find_location_from_marker (*m, is_start);
4673 (*m)->set_position (is_start ? location->start() : location->end());
4680 MarkerDrag::update_item (Location*)
4685 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4687 , _fixed_grab_x (0.0)
4688 , _fixed_grab_y (0.0)
4689 , _cumulative_x_drag (0.0)
4690 , _cumulative_y_drag (0.0)
4694 if (_zero_gain_fraction < 0.0) {
4695 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4698 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4700 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4706 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4708 Drag::start_grab (event, _editor->cursors()->fader);
4710 // start the grab at the center of the control point so
4711 // the point doesn't 'jump' to the mouse after the first drag
4712 _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4713 _fixed_grab_y = _point->get_y();
4715 setup_snap_delta (MusicFrame (_editor->pixel_to_sample (_fixed_grab_x), 0));
4717 float const fraction = 1 - (_point->get_y() / _point->line().height());
4718 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4720 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4722 if (!_point->can_slide ()) {
4723 _x_constrained = true;
4728 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4730 double dx = _drags->current_pointer_x() - last_pointer_x();
4731 double dy = current_pointer_y() - last_pointer_y();
4732 bool need_snap = true;
4734 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4740 /* coordinate in pixels relative to the start of the region (for region-based automation)
4741 or track (for track-based automation) */
4742 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4743 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4745 // calculate zero crossing point. back off by .01 to stay on the
4746 // positive side of zero
4747 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4749 if (_x_constrained) {
4752 if (_y_constrained) {
4756 _cumulative_x_drag = cx - _fixed_grab_x;
4757 _cumulative_y_drag = cy - _fixed_grab_y;
4761 cy = min ((double) _point->line().height(), cy);
4763 // make sure we hit zero when passing through
4764 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4768 MusicFrame cx_mf (_editor->pixel_to_sample (cx) + snap_delta (event->button.state), 0);
4770 if (!_x_constrained && need_snap) {
4771 _editor->snap_to_with_modifier (cx_mf, event);
4774 cx_mf.frame -= snap_delta (event->button.state);
4775 cx_mf.frame = min (cx_mf.frame, _point->line().maximum_time() + _point->line().offset());
4777 float const fraction = 1.0 - (cy / _point->line().height());
4780 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4781 _editor->begin_reversible_command (_("automation event move"));
4782 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4784 pair<double, float> result;
4785 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_mf.frame), fraction, false, _pushing, _final_index);
4787 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4791 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4793 if (!movement_occurred) {
4796 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4797 _editor->reset_point_selection ();
4801 _point->line().end_drag (_pushing, _final_index);
4802 _editor->commit_reversible_command ();
4807 ControlPointDrag::aborted (bool)
4809 _point->line().reset ();
4813 ControlPointDrag::active (Editing::MouseMode m)
4815 if (m == Editing::MouseDraw) {
4816 /* always active in mouse draw */
4820 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4821 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4824 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4827 , _fixed_grab_x (0.0)
4828 , _fixed_grab_y (0.0)
4829 , _cumulative_y_drag (0)
4833 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4837 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4839 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4842 _item = &_line->grab_item ();
4844 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4845 origin, and ditto for y.
4848 double mx = event->button.x;
4849 double my = event->button.y;
4851 _line->grab_item().canvas_to_item (mx, my);
4853 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4855 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4856 /* no adjacent points */
4860 Drag::start_grab (event, _editor->cursors()->fader);
4862 /* store grab start in item frame */
4863 double const bx = _line->nth (_before)->get_x();
4864 double const ax = _line->nth (_after)->get_x();
4865 double const click_ratio = (ax - mx) / (ax - bx);
4867 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4872 double fraction = 1.0 - (cy / _line->height());
4874 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4878 LineDrag::motion (GdkEvent* event, bool first_move)
4880 double dy = current_pointer_y() - last_pointer_y();
4882 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4886 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4888 _cumulative_y_drag = cy - _fixed_grab_y;
4891 cy = min ((double) _line->height(), cy);
4893 double const fraction = 1.0 - (cy / _line->height());
4897 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4899 _editor->begin_reversible_command (_("automation range move"));
4900 _line->start_drag_line (_before, _after, initial_fraction);
4903 /* we are ignoring x position for this drag, so we can just pass in anything */
4904 pair<double, float> result;
4906 result = _line->drag_motion (0, fraction, true, false, ignored);
4907 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4911 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4913 if (movement_occurred) {
4914 motion (event, false);
4915 _line->end_drag (false, 0);
4916 _editor->commit_reversible_command ();
4918 /* add a new control point on the line */
4920 AutomationTimeAxisView* atv;
4922 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4923 framepos_t where = grab_frame ();
4926 double cy = _fixed_grab_y;
4928 _line->grab_item().item_to_canvas (cx, cy);
4930 atv->add_automation_event (event, where, cy, false);
4931 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4932 AudioRegionView* arv;
4934 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4935 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4942 LineDrag::aborted (bool)
4947 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4951 _region_view_grab_x (0.0),
4952 _cumulative_x_drag (0),
4956 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4960 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4962 Drag::start_grab (event);
4964 _line = reinterpret_cast<Line*> (_item);
4967 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4969 double cx = event->button.x;
4970 double cy = event->button.y;
4972 _item->parent()->canvas_to_item (cx, cy);
4974 /* store grab start in parent frame */
4975 _region_view_grab_x = cx;
4977 _before = *(float*) _item->get_data ("position");
4979 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4981 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4985 FeatureLineDrag::motion (GdkEvent*, bool)
4987 double dx = _drags->current_pointer_x() - last_pointer_x();
4989 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4991 _cumulative_x_drag += dx;
4993 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
5002 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
5004 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
5006 float *pos = new float;
5009 _line->set_data ("position", pos);
5015 FeatureLineDrag::finished (GdkEvent*, bool)
5017 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
5018 _arv->update_transient(_before, _before);
5022 FeatureLineDrag::aborted (bool)
5027 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5029 , _vertical_only (false)
5031 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
5035 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5037 Drag::start_grab (event);
5038 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
5042 RubberbandSelectDrag::motion (GdkEvent* event, bool)
5048 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
5049 MusicFrame grab (grab_frame (), 0);
5051 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
5052 _editor->snap_to_with_modifier (grab, event);
5054 grab.frame = raw_grab_frame ();
5057 /* base start and end on initial click position */
5059 if (pf < grab.frame) {
5067 if (current_pointer_y() < grab_y()) {
5068 y1 = current_pointer_y();
5071 y2 = current_pointer_y();
5075 if (start != end || y1 != y2) {
5077 double x1 = _editor->sample_to_pixel (start);
5078 double x2 = _editor->sample_to_pixel (end);
5079 const double min_dimension = 2.0;
5081 if (_vertical_only) {
5082 /* fixed 10 pixel width */
5086 x2 = min (x1 - min_dimension, x2);
5088 x2 = max (x1 + min_dimension, x2);
5093 y2 = min (y1 - min_dimension, y2);
5095 y2 = max (y1 + min_dimension, y2);
5098 /* translate rect into item space and set */
5100 ArdourCanvas::Rect r (x1, y1, x2, y2);
5102 /* this drag is a _trackview_only == true drag, so the y1 and
5103 * y2 (computed using current_pointer_y() and grab_y()) will be
5104 * relative to the top of the trackview group). The
5105 * rubberband rect has the same parent/scroll offset as the
5106 * the trackview group, so we can use the "r" rect directly
5107 * to set the shape of the rubberband.
5110 _editor->rubberband_rect->set (r);
5111 _editor->rubberband_rect->show();
5112 _editor->rubberband_rect->raise_to_top();
5114 show_verbose_cursor_time (pf);
5116 do_select_things (event, true);
5121 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
5125 framepos_t grab = grab_frame ();
5126 framepos_t lpf = last_pointer_frame ();
5128 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
5129 grab = raw_grab_frame ();
5130 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
5144 if (current_pointer_y() < grab_y()) {
5145 y1 = current_pointer_y();
5148 y2 = current_pointer_y();
5152 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
5156 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
5158 if (movement_occurred) {
5160 motion (event, false);
5161 do_select_things (event, false);
5167 bool do_deselect = true;
5168 MidiTimeAxisView* mtv;
5170 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
5172 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
5173 /* nothing selected */
5174 add_midi_region (mtv, true);
5175 do_deselect = false;
5179 /* do not deselect if Primary or Tertiary (toggle-select or
5180 * extend-select are pressed.
5183 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
5184 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
5191 _editor->rubberband_rect->hide();
5195 RubberbandSelectDrag::aborted (bool)
5197 _editor->rubberband_rect->hide ();
5200 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
5201 : RegionDrag (e, i, p, v)
5203 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
5207 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5209 Drag::start_grab (event, cursor);
5211 _editor->get_selection().add (_primary);
5213 MusicFrame where (_primary->region()->position(), 0);
5214 setup_snap_delta (where);
5216 show_verbose_cursor_duration (where.frame, adjusted_current_frame (event), 0);
5220 TimeFXDrag::motion (GdkEvent* event, bool)
5222 RegionView* rv = _primary;
5223 StreamView* cv = rv->get_time_axis_view().view ();
5224 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
5225 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
5226 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
5227 MusicFrame pf (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
5229 _editor->snap_to_with_modifier (pf, event);
5230 pf.frame -= snap_delta (event->button.state);
5232 if (pf.frame > rv->region()->position()) {
5233 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf.frame, layers, layer);
5236 show_verbose_cursor_duration (_primary->region()->position(), pf.frame, 0);
5240 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
5242 /* this may have been a single click, no drag. We still want the dialog
5243 to show up in that case, so that the user can manually edit the
5244 parameters for the timestretch.
5247 float fraction = 1.0;
5249 if (movement_occurred) {
5251 motion (event, false);
5253 _primary->get_time_axis_view().hide_timestretch ();
5255 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
5257 if (adjusted_frame_pos < _primary->region()->position()) {
5258 /* backwards drag of the left edge - not usable */
5262 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
5264 fraction = (double) newlen / (double) _primary->region()->length();
5266 #ifndef USE_RUBBERBAND
5267 // Soundtouch uses fraction / 100 instead of normal (/ 1)
5268 if (_primary->region()->data_type() == DataType::AUDIO) {
5269 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
5274 if (!_editor->get_selection().regions.empty()) {
5275 /* primary will already be included in the selection, and edit
5276 group shared editing will propagate selection across
5277 equivalent regions, so just use the current region
5281 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
5282 error << _("An error occurred while executing time stretch operation") << endmsg;
5288 TimeFXDrag::aborted (bool)
5290 _primary->get_time_axis_view().hide_timestretch ();
5293 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
5296 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
5300 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5302 Drag::start_grab (event);
5306 ScrubDrag::motion (GdkEvent* /*event*/, bool)
5308 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
5312 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
5314 if (movement_occurred && _editor->session()) {
5315 /* make sure we stop */
5316 _editor->session()->request_transport_speed (0.0);
5321 ScrubDrag::aborted (bool)
5326 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5330 , _track_selection_at_start (e)
5331 , _time_selection_at_start (!_editor->get_selection().time.empty())
5333 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
5335 if (_time_selection_at_start) {
5336 start_at_start = _editor->get_selection().time.start();
5337 end_at_start = _editor->get_selection().time.end_frame();
5342 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
5344 if (_editor->session() == 0) {
5348 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5350 switch (_operation) {
5351 case CreateSelection:
5352 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5357 cursor = _editor->cursors()->selector;
5358 Drag::start_grab (event, cursor);
5361 case SelectionStartTrim:
5362 if (_editor->clicked_axisview) {
5363 _editor->clicked_axisview->order_selection_trims (_item, true);
5365 Drag::start_grab (event, _editor->cursors()->left_side_trim);
5368 case SelectionEndTrim:
5369 if (_editor->clicked_axisview) {
5370 _editor->clicked_axisview->order_selection_trims (_item, false);
5372 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5376 Drag::start_grab (event, cursor);
5379 case SelectionExtend:
5380 Drag::start_grab (event, cursor);
5384 if (_operation == SelectionMove) {
5385 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5387 show_verbose_cursor_time (adjusted_current_frame (event));
5392 SelectionDrag::setup_pointer_frame_offset ()
5394 switch (_operation) {
5395 case CreateSelection:
5396 _pointer_frame_offset = 0;
5399 case SelectionStartTrim:
5401 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5404 case SelectionEndTrim:
5405 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5408 case SelectionExtend:
5414 SelectionDrag::motion (GdkEvent* event, bool first_move)
5416 framepos_t start = 0;
5418 framecnt_t length = 0;
5419 framecnt_t distance = 0;
5420 MusicFrame start_mf (0, 0);
5421 framepos_t const pending_position = adjusted_current_frame (event);
5423 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5428 _track_selection_at_start = _editor->selection->tracks;
5431 switch (_operation) {
5432 case CreateSelection:
5434 MusicFrame grab (grab_frame (), 0);
5436 grab.frame = adjusted_current_frame (event, false);
5437 if (grab.frame < pending_position) {
5438 _editor->snap_to (grab, RoundDownMaybe);
5440 _editor->snap_to (grab, RoundUpMaybe);
5444 if (pending_position < grab.frame) {
5445 start = pending_position;
5448 end = pending_position;
5452 /* first drag: Either add to the selection
5453 or create a new selection
5460 /* adding to the selection */
5461 _editor->set_selected_track_as_side_effect (Selection::Add);
5462 _editor->clicked_selection = _editor->selection->add (start, end);
5469 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5470 _editor->set_selected_track_as_side_effect (Selection::Set);
5473 _editor->clicked_selection = _editor->selection->set (start, end);
5477 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5478 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5479 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5481 _editor->selection->add (atest);
5485 /* select all tracks within the rectangle that we've marked out so far */
5486 TrackViewList new_selection;
5487 TrackViewList& all_tracks (_editor->track_views);
5489 ArdourCanvas::Coord const top = grab_y();
5490 ArdourCanvas::Coord const bottom = current_pointer_y();
5492 if (top >= 0 && bottom >= 0) {
5494 //first, find the tracks that are covered in the y range selection
5495 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5496 if ((*i)->covered_by_y_range (top, bottom)) {
5497 new_selection.push_back (*i);
5501 //now compare our list with the current selection, and add as necessary
5502 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5503 TrackViewList tracks_to_add;
5504 TrackViewList tracks_to_remove;
5505 vector<RouteGroup*> selected_route_groups;
5508 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i) {
5509 if (!new_selection.contains (*i) && !_track_selection_at_start.contains (*i)) {
5510 tracks_to_remove.push_back (*i);
5512 RouteGroup* rg = (*i)->route_group();
5513 if (rg && rg->is_active() && rg->is_select()) {
5514 selected_route_groups.push_back (rg);
5520 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5521 if (!_editor->selection->tracks.contains (*i)) {
5522 tracks_to_add.push_back (*i);
5523 RouteGroup* rg = (*i)->route_group();
5525 if (rg && rg->is_active() && rg->is_select()) {
5526 selected_route_groups.push_back (rg);
5531 _editor->selection->add (tracks_to_add);
5533 if (!tracks_to_remove.empty()) {
5535 /* check all these to-be-removed tracks against the
5536 * possibility that they are selected by being
5537 * in the same group as an approved track.
5540 for (TrackViewList::iterator i = tracks_to_remove.begin(); i != tracks_to_remove.end(); ) {
5541 RouteGroup* rg = (*i)->route_group();
5543 if (rg && find (selected_route_groups.begin(), selected_route_groups.end(), rg) != selected_route_groups.end()) {
5544 i = tracks_to_remove.erase (i);
5550 /* remove whatever is left */
5552 _editor->selection->remove (tracks_to_remove);
5558 case SelectionStartTrim:
5560 end = _editor->selection->time[_editor->clicked_selection].end;
5562 if (pending_position > end) {
5565 start = pending_position;
5569 case SelectionEndTrim:
5571 start = _editor->selection->time[_editor->clicked_selection].start;
5573 if (pending_position < start) {
5576 end = pending_position;
5583 start = _editor->selection->time[_editor->clicked_selection].start;
5584 end = _editor->selection->time[_editor->clicked_selection].end;
5586 length = end - start;
5587 distance = pending_position - start;
5588 start = pending_position;
5590 start_mf.frame = start;
5591 _editor->snap_to (start_mf);
5593 end = start_mf.frame + length;
5597 case SelectionExtend:
5602 switch (_operation) {
5604 if (_time_selection_at_start) {
5605 _editor->selection->move_time (distance);
5609 _editor->selection->replace (_editor->clicked_selection, start, end);
5613 if (_operation == SelectionMove) {
5614 show_verbose_cursor_time(start);
5616 show_verbose_cursor_time(pending_position);
5621 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5623 Session* s = _editor->session();
5625 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5626 if (movement_occurred) {
5627 motion (event, false);
5628 /* XXX this is not object-oriented programming at all. ick */
5629 if (_editor->selection->time.consolidate()) {
5630 _editor->selection->TimeChanged ();
5633 /* XXX what if its a music time selection? */
5635 if (s->get_play_range() && s->transport_rolling()) {
5636 s->request_play_range (&_editor->selection->time, true);
5637 } else if (!s->config.get_external_sync()) {
5638 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5639 s->request_locate (_editor->get_selection().time.start());
5643 if (_editor->get_selection().time.length() != 0) {
5644 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5646 s->clear_range_selection ();
5651 /* just a click, no pointer movement.
5654 if (was_double_click()) {
5655 if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
5656 _editor->temporal_zoom_selection (Both);
5661 if (_operation == SelectionExtend) {
5662 if (_time_selection_at_start) {
5663 framepos_t pos = adjusted_current_frame (event, false);
5664 framepos_t start = min (pos, start_at_start);
5665 framepos_t end = max (pos, end_at_start);
5666 _editor->selection->set (start, end);
5669 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5670 if (_editor->clicked_selection) {
5671 _editor->selection->remove (_editor->clicked_selection);
5674 if (!_editor->clicked_selection) {
5675 _editor->selection->clear_time();
5680 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5681 _editor->selection->set (_editor->clicked_axisview);
5684 if (s && s->get_play_range () && s->transport_rolling()) {
5685 s->request_stop (false, false);
5690 _editor->stop_canvas_autoscroll ();
5691 _editor->clicked_selection = 0;
5692 _editor->commit_reversible_selection_op ();
5696 SelectionDrag::aborted (bool)
5701 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5702 : Drag (e, i, false),
5706 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5708 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5709 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5710 physical_screen_height (_editor->current_toplevel()->get_window())));
5711 _drag_rect->hide ();
5713 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5714 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5717 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5719 /* normal canvas items will be cleaned up when their parent group is deleted. But
5720 this item is created as the child of a long-lived parent group, and so we
5721 need to explicitly delete it.
5727 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5729 if (_editor->session() == 0) {
5733 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5735 if (!_editor->temp_location) {
5736 _editor->temp_location = new Location (*_editor->session());
5739 switch (_operation) {
5740 case CreateSkipMarker:
5741 case CreateRangeMarker:
5742 case CreateTransportMarker:
5743 case CreateCDMarker:
5745 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5750 cursor = _editor->cursors()->selector;
5754 Drag::start_grab (event, cursor);
5756 show_verbose_cursor_time (adjusted_current_frame (event));
5760 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5762 framepos_t start = 0;
5764 ArdourCanvas::Rectangle *crect;
5766 switch (_operation) {
5767 case CreateSkipMarker:
5768 crect = _editor->range_bar_drag_rect;
5770 case CreateRangeMarker:
5771 crect = _editor->range_bar_drag_rect;
5773 case CreateTransportMarker:
5774 crect = _editor->transport_bar_drag_rect;
5776 case CreateCDMarker:
5777 crect = _editor->cd_marker_bar_drag_rect;
5780 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5785 framepos_t const pf = adjusted_current_frame (event);
5787 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5788 MusicFrame grab (grab_frame (), 0);
5789 _editor->snap_to (grab);
5791 if (pf < grab_frame()) {
5799 /* first drag: Either add to the selection
5800 or create a new selection.
5805 _editor->temp_location->set (start, end);
5809 update_item (_editor->temp_location);
5811 //_drag_rect->raise_to_top();
5817 _editor->temp_location->set (start, end);
5819 double x1 = _editor->sample_to_pixel (start);
5820 double x2 = _editor->sample_to_pixel (end);
5824 update_item (_editor->temp_location);
5827 show_verbose_cursor_time (pf);
5832 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5834 Location * newloc = 0;
5838 if (movement_occurred) {
5839 motion (event, false);
5842 switch (_operation) {
5843 case CreateSkipMarker:
5844 case CreateRangeMarker:
5845 case CreateCDMarker:
5847 XMLNode &before = _editor->session()->locations()->get_state();
5848 if (_operation == CreateSkipMarker) {
5849 _editor->begin_reversible_command (_("new skip marker"));
5850 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5851 flags = Location::IsRangeMarker | Location::IsSkip;
5852 _editor->range_bar_drag_rect->hide();
5853 } else if (_operation == CreateCDMarker) {
5854 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5855 _editor->begin_reversible_command (_("new CD marker"));
5856 flags = Location::IsRangeMarker | Location::IsCDMarker;
5857 _editor->cd_marker_bar_drag_rect->hide();
5859 _editor->begin_reversible_command (_("new skip marker"));
5860 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5861 flags = Location::IsRangeMarker;
5862 _editor->range_bar_drag_rect->hide();
5864 newloc = new Location (
5865 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5866 , _editor->get_grid_music_divisions (event->button.state));
5868 _editor->session()->locations()->add (newloc, true);
5869 XMLNode &after = _editor->session()->locations()->get_state();
5870 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5871 _editor->commit_reversible_command ();
5875 case CreateTransportMarker:
5876 // popup menu to pick loop or punch
5877 _editor->new_transport_marker_context_menu (&event->button, _item);
5883 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5885 if (_operation == CreateTransportMarker) {
5887 /* didn't drag, so just locate */
5889 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5891 } else if (_operation == CreateCDMarker) {
5893 /* didn't drag, but mark is already created so do
5896 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5901 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5903 if (end == max_framepos) {
5904 end = _editor->session()->current_end_frame ();
5907 if (start == max_framepos) {
5908 start = _editor->session()->current_start_frame ();
5911 switch (_editor->mouse_mode) {
5913 /* find the two markers on either side and then make the selection from it */
5914 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5918 /* find the two markers on either side of the click and make the range out of it */
5919 _editor->selection->set (start, end);
5928 _editor->stop_canvas_autoscroll ();
5932 RangeMarkerBarDrag::aborted (bool movement_occurred)
5934 if (movement_occurred) {
5935 _drag_rect->hide ();
5940 RangeMarkerBarDrag::update_item (Location* location)
5942 double const x1 = _editor->sample_to_pixel (location->start());
5943 double const x2 = _editor->sample_to_pixel (location->end());
5945 _drag_rect->set_x0 (x1);
5946 _drag_rect->set_x1 (x2);
5949 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5951 , _cumulative_dx (0)
5952 , _cumulative_dy (0)
5954 , _was_selected (false)
5957 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5959 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5961 _region = &_primary->region_view ();
5962 _note_height = _region->midi_stream_view()->note_height ();
5966 NoteDrag::setup_pointer_frame_offset ()
5968 _pointer_frame_offset = raw_grab_frame()
5969 - _editor->session()->tempo_map().frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
5973 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5975 Drag::start_grab (event);
5977 if (ArdourKeyboard::indicates_copy (event->button.state)) {
5983 setup_snap_delta (MusicFrame (_region->source_beats_to_absolute_frames (_primary->note()->time ()), 0));
5985 if (!(_was_selected = _primary->selected())) {
5987 /* tertiary-click means extend selection - we'll do that on button release,
5988 so don't add it here, because otherwise we make it hard to figure
5989 out the "extend-to" range.
5992 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5995 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5998 _region->note_selected (_primary, true);
6000 _editor->get_selection().clear_points();
6001 _region->unique_select (_primary);
6007 /** @return Current total drag x change in quarter notes */
6009 NoteDrag::total_dx (GdkEvent * event) const
6011 if (_x_constrained) {
6015 TempoMap& map (_editor->session()->tempo_map());
6018 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
6020 /* primary note time */
6021 frameoffset_t const n = map.frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
6023 /* primary note time in quarter notes */
6024 double const n_qn = _region->session_relative_qn (_primary->note()->time().to_double());
6026 /* new time of the primary note in session frames */
6027 frameoffset_t st = n + dx + snap_delta (event->button.state);
6029 /* possibly snap and return corresponding delta in quarter notes */
6030 MusicFrame snap (st, 0);
6031 _editor->snap_to_with_modifier (snap, event);
6032 double ret = map.exact_qn_at_frame (snap.frame, snap.division) - n_qn - snap_delta_music (event->button.state);
6034 /* prevent the earliest note being dragged earlier than the region's start position */
6035 if (_earliest + ret < _region->midi_region()->start_beats()) {
6036 ret -= (_earliest + ret) - _region->midi_region()->start_beats();
6042 /** @return Current total drag y change in note number */
6044 NoteDrag::total_dy () const
6046 if (_y_constrained) {
6050 double const y = _region->midi_view()->y_position ();
6051 /* new current note */
6052 uint8_t n = _region->y_to_note (current_pointer_y () - y);
6054 MidiStreamView* msv = _region->midi_stream_view ();
6055 n = max (msv->lowest_note(), n);
6056 n = min (msv->highest_note(), n);
6057 /* and work out delta */
6058 return n - _region->y_to_note (grab_y() - y);
6062 NoteDrag::motion (GdkEvent * event, bool first_move)
6065 _earliest = _region->earliest_in_selection().to_double();
6067 /* make copies of all the selected notes */
6068 _primary = _region->copy_selection (_primary);
6072 /* Total change in x and y since the start of the drag */
6073 double const dx_qn = total_dx (event);
6074 int8_t const dy = total_dy ();
6076 /* Now work out what we have to do to the note canvas items to set this new drag delta */
6077 double const tdx = _x_constrained ? 0 : dx_qn - _cumulative_dx;
6078 double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
6081 _cumulative_dx = dx_qn;
6082 _cumulative_dy += tdy;
6084 int8_t note_delta = total_dy();
6088 _region->move_copies (dx_qn, tdy, note_delta);
6090 _region->move_selection (dx_qn, tdy, note_delta);
6093 /* the new note value may be the same as the old one, but we
6094 * don't know what that means because the selection may have
6095 * involved more than one note and we might be doing something
6096 * odd with them. so show the note value anyway, always.
6099 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
6101 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
6107 NoteDrag::finished (GdkEvent* ev, bool moved)
6110 /* no motion - select note */
6112 if (_editor->current_mouse_mode() == Editing::MouseContent ||
6113 _editor->current_mouse_mode() == Editing::MouseDraw) {
6115 bool changed = false;
6117 if (_was_selected) {
6118 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
6120 _region->note_deselected (_primary);
6123 _editor->get_selection().clear_points();
6124 _region->unique_select (_primary);
6128 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
6129 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
6131 if (!extend && !add && _region->selection_size() > 1) {
6132 _editor->get_selection().clear_points();
6133 _region->unique_select (_primary);
6135 } else if (extend) {
6136 _region->note_selected (_primary, true, true);
6139 /* it was added during button press */
6146 _editor->begin_reversible_selection_op(X_("Select Note Release"));
6147 _editor->commit_reversible_selection_op();
6151 _region->note_dropped (_primary, total_dx (ev), total_dy(), _copy);
6156 NoteDrag::aborted (bool)
6161 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
6162 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
6163 : Drag (editor, atv->base_item ())
6165 , _y_origin (atv->y_position())
6166 , _nothing_to_drag (false)
6168 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
6169 setup (atv->lines ());
6172 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
6173 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
6174 : Drag (editor, rv->get_canvas_group ())
6176 , _y_origin (rv->get_time_axis_view().y_position())
6177 , _nothing_to_drag (false)
6180 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
6182 list<boost::shared_ptr<AutomationLine> > lines;
6184 AudioRegionView* audio_view;
6185 AutomationRegionView* automation_view;
6186 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
6187 lines.push_back (audio_view->get_gain_line ());
6188 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
6189 lines.push_back (automation_view->line ());
6192 error << _("Automation range drag created for invalid region type") << endmsg;
6198 /** @param lines AutomationLines to drag.
6199 * @param offset Offset from the session start to the points in the AutomationLines.
6202 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
6204 /* find the lines that overlap the ranges being dragged */
6205 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
6206 while (i != lines.end ()) {
6207 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
6210 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
6212 /* check this range against all the AudioRanges that we are using */
6213 list<AudioRange>::const_iterator k = _ranges.begin ();
6214 while (k != _ranges.end()) {
6215 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
6221 /* add it to our list if it overlaps at all */
6222 if (k != _ranges.end()) {
6227 _lines.push_back (n);
6233 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
6237 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
6239 return 1.0 - ((global_y - _y_origin) / line->height());
6243 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
6245 const double v = list->eval(x);
6246 return _integral ? rint(v) : v;
6250 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6252 Drag::start_grab (event, cursor);
6254 /* Get line states before we start changing things */
6255 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6256 i->state = &i->line->get_state ();
6257 i->original_fraction = y_fraction (i->line, current_pointer_y());
6260 if (_ranges.empty()) {
6262 /* No selected time ranges: drag all points */
6263 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6264 uint32_t const N = i->line->npoints ();
6265 for (uint32_t j = 0; j < N; ++j) {
6266 i->points.push_back (i->line->nth (j));
6272 if (_nothing_to_drag) {
6278 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
6280 if (_nothing_to_drag && !first_move) {
6285 _editor->begin_reversible_command (_("automation range move"));
6287 if (!_ranges.empty()) {
6289 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
6291 framecnt_t const half = (i->start + i->end) / 2;
6293 /* find the line that this audio range starts in */
6294 list<Line>::iterator j = _lines.begin();
6295 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
6299 if (j != _lines.end()) {
6300 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6302 /* j is the line that this audio range starts in; fade into it;
6303 64 samples length plucked out of thin air.
6306 framepos_t a = i->start + 64;
6311 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
6312 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
6314 XMLNode &before = the_list->get_state();
6315 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6316 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6318 if (add_p || add_q) {
6319 _editor->session()->add_command (
6320 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6324 /* same thing for the end */
6327 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
6331 if (j != _lines.end()) {
6332 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6334 /* j is the line that this audio range starts in; fade out of it;
6335 64 samples length plucked out of thin air.
6338 framepos_t b = i->end - 64;
6343 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
6344 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
6346 XMLNode &before = the_list->get_state();
6347 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6348 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6350 if (add_p || add_q) {
6351 _editor->session()->add_command (
6352 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6357 _nothing_to_drag = true;
6359 /* Find all the points that should be dragged and put them in the relevant
6360 points lists in the Line structs.
6363 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6365 uint32_t const N = i->line->npoints ();
6366 for (uint32_t j = 0; j < N; ++j) {
6368 /* here's a control point on this line */
6369 ControlPoint* p = i->line->nth (j);
6370 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
6372 /* see if it's inside a range */
6373 list<AudioRange>::const_iterator k = _ranges.begin ();
6374 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
6378 if (k != _ranges.end()) {
6379 /* dragging this point */
6380 _nothing_to_drag = false;
6381 i->points.push_back (p);
6387 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6388 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
6392 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
6393 float const f = y_fraction (l->line, current_pointer_y());
6394 /* we are ignoring x position for this drag, so we can just pass in anything */
6395 pair<double, float> result;
6397 result = l->line->drag_motion (0, f, true, false, ignored);
6398 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
6403 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
6405 if (_nothing_to_drag || !motion_occurred) {
6409 motion (event, false);
6410 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6411 i->line->end_drag (false, 0);
6414 _editor->commit_reversible_command ();
6418 AutomationRangeDrag::aborted (bool)
6420 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6425 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6427 , initial_time_axis_view (itav)
6429 /* note that time_axis_view may be null if the regionview was created
6430 * as part of a copy operation.
6432 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6433 layer = v->region()->layer ();
6434 initial_y = v->get_canvas_group()->position().y;
6435 initial_playlist = v->region()->playlist ();
6436 initial_position = v->region()->position ();
6437 initial_end = v->region()->position () + v->region()->length ();
6440 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6441 : Drag (e, i->canvas_item ())
6444 , _cumulative_dx (0)
6446 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6447 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6452 PatchChangeDrag::motion (GdkEvent* ev, bool)
6454 framepos_t f = adjusted_current_frame (ev);
6455 boost::shared_ptr<Region> r = _region_view->region ();
6456 f = max (f, r->position ());
6457 f = min (f, r->last_frame ());
6459 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6460 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6461 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6462 _cumulative_dx = dxu;
6466 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6468 if (!movement_occurred) {
6469 if (was_double_click()) {
6470 _region_view->edit_patch_change (_patch_change);
6475 boost::shared_ptr<Region> r (_region_view->region ());
6476 framepos_t f = adjusted_current_frame (ev);
6477 f = max (f, r->position ());
6478 f = min (f, r->last_frame ());
6480 _region_view->move_patch_change (
6482 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6487 PatchChangeDrag::aborted (bool)
6489 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6493 PatchChangeDrag::setup_pointer_frame_offset ()
6495 boost::shared_ptr<Region> region = _region_view->region ();
6496 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6499 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6500 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6507 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6509 _region_view->update_drag_selection (
6511 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6515 MidiRubberbandSelectDrag::deselect_things ()
6520 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6521 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6524 _vertical_only = true;
6528 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6530 double const y = _region_view->midi_view()->y_position ();
6532 y1 = max (0.0, y1 - y);
6533 y2 = max (0.0, y2 - y);
6535 _region_view->update_vertical_drag_selection (
6538 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6543 MidiVerticalSelectDrag::deselect_things ()
6548 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6549 : RubberbandSelectDrag (e, i)
6555 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6557 if (drag_in_progress) {
6558 /* We just want to select things at the end of the drag, not during it */
6562 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6564 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6566 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6568 _editor->commit_reversible_selection_op ();
6572 EditorRubberbandSelectDrag::deselect_things ()
6574 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6576 _editor->selection->clear_tracks();
6577 _editor->selection->clear_regions();
6578 _editor->selection->clear_points ();
6579 _editor->selection->clear_lines ();
6580 _editor->selection->clear_midi_notes ();
6582 _editor->commit_reversible_selection_op();
6585 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6590 _note[0] = _note[1] = 0;
6593 NoteCreateDrag::~NoteCreateDrag ()
6599 NoteCreateDrag::grid_frames (framepos_t t) const
6602 const Evoral::Beats grid_beats = _region_view->get_grid_beats (t);
6603 const Evoral::Beats t_beats = _region_view->region_frames_to_region_beats (t);
6605 return _region_view->region_beats_to_region_frames (t_beats + grid_beats)
6606 - _region_view->region_beats_to_region_frames (t_beats);
6610 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6612 Drag::start_grab (event, cursor);
6614 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6615 TempoMap& map (_editor->session()->tempo_map());
6617 const framepos_t pf = _drags->current_pointer_frame ();
6618 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6620 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6622 double eqaf = map.exact_qn_at_frame (pf, divisions);
6624 if (divisions != 0) {
6626 const double qaf = map.quarter_note_at_frame (pf);
6628 /* Hack so that we always snap to the note that we are over, instead of snapping
6629 to the next one if we're more than halfway through the one we're over.
6632 const double rem = eqaf - qaf;
6634 eqaf -= grid_beats.to_double();
6638 _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6639 /* minimum initial length is grid beats */
6640 _note[1] = map.frame_at_quarter_note (eqaf + grid_beats.to_double()) - _region_view->region()->position();
6642 double const x0 = _editor->sample_to_pixel (_note[0]);
6643 double const x1 = _editor->sample_to_pixel (_note[1]);
6644 double const y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6646 _drag_rect->set (ArdourCanvas::Rect (x0, y, x1, y + floor (_region_view->midi_stream_view()->note_height ())));
6647 _drag_rect->set_outline_all ();
6648 _drag_rect->set_outline_color (0xffffff99);
6649 _drag_rect->set_fill_color (0xffffff66);
6653 NoteCreateDrag::motion (GdkEvent* event, bool)
6655 TempoMap& map (_editor->session()->tempo_map());
6656 const framepos_t pf = _drags->current_pointer_frame ();
6657 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6658 double eqaf = map.exact_qn_at_frame (pf, divisions);
6660 if (divisions != 0) {
6662 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6664 const double qaf = map.quarter_note_at_frame (pf);
6665 /* Hack so that we always snap to the note that we are over, instead of snapping
6666 to the next one if we're more than halfway through the one we're over.
6669 const double rem = eqaf - qaf;
6671 eqaf -= grid_beats.to_double();
6674 eqaf += grid_beats.to_double();
6676 _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6678 double const x0 = _editor->sample_to_pixel (_note[0]);
6679 double const x1 = _editor->sample_to_pixel (_note[1]);
6680 _drag_rect->set_x0 (std::min(x0, x1));
6681 _drag_rect->set_x1 (std::max(x0, x1));
6685 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6687 /* we create a note even if there was no movement */
6688 framepos_t const start = min (_note[0], _note[1]);
6689 framepos_t const start_sess_rel = start + _region_view->region()->position();
6690 framecnt_t length = max (_editor->pixel_to_sample (1.0), (framecnt_t) fabs ((double)(_note[0] - _note[1])));
6691 framecnt_t const g = grid_frames (start);
6693 if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6697 TempoMap& map (_editor->session()->tempo_map());
6698 const double qn_length = map.quarter_notes_between_frames (start_sess_rel, start_sess_rel + length);
6699 Evoral::Beats qn_length_beats = max (Evoral::Beats::ticks(1), Evoral::Beats (qn_length));
6701 _editor->begin_reversible_command (_("Create Note"));
6702 _region_view->clear_editor_note_selection();
6703 _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6704 _editor->commit_reversible_command ();
6708 NoteCreateDrag::y_to_region (double y) const
6711 _region_view->get_canvas_group()->canvas_to_item (x, y);
6716 NoteCreateDrag::aborted (bool)
6721 HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6729 HitCreateDrag::~HitCreateDrag ()
6734 HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6736 Drag::start_grab (event, cursor);
6738 TempoMap& map (_editor->session()->tempo_map());
6740 _y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6742 const framepos_t pf = _drags->current_pointer_frame ();
6743 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6745 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6747 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6749 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6753 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6754 Evoral::Beats length = _region_view->get_grid_beats (pf);
6756 _editor->begin_reversible_command (_("Create Hit"));
6757 _region_view->clear_editor_note_selection();
6758 _region_view->create_note_at (start, _y, length, event->button.state, false);
6764 HitCreateDrag::motion (GdkEvent* event, bool)
6766 TempoMap& map (_editor->session()->tempo_map());
6768 const framepos_t pf = _drags->current_pointer_frame ();
6769 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6771 if (divisions == 0) {
6775 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6776 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position ();
6778 if (_last_pos == start) {
6782 Evoral::Beats length = _region_view->get_grid_beats (pf);
6784 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6786 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6790 _region_view->create_note_at (start, _y, length, event->button.state, false);
6796 HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */)
6798 _editor->commit_reversible_command ();
6803 HitCreateDrag::y_to_region (double y) const
6806 _region_view->get_canvas_group()->canvas_to_item (x, y);
6811 HitCreateDrag::aborted (bool)
6816 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6821 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6825 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6827 Drag::start_grab (event, cursor);
6831 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6837 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6840 distance = _drags->current_pointer_x() - grab_x();
6841 len = ar->fade_in()->back()->when;
6843 distance = grab_x() - _drags->current_pointer_x();
6844 len = ar->fade_out()->back()->when;
6847 /* how long should it be ? */
6849 new_length = len + _editor->pixel_to_sample (distance);
6851 /* now check with the region that this is legal */
6853 new_length = ar->verify_xfade_bounds (new_length, start);
6856 arv->reset_fade_in_shape_width (ar, new_length);
6858 arv->reset_fade_out_shape_width (ar, new_length);
6863 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6869 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6872 distance = _drags->current_pointer_x() - grab_x();
6873 len = ar->fade_in()->back()->when;
6875 distance = grab_x() - _drags->current_pointer_x();
6876 len = ar->fade_out()->back()->when;
6879 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6881 _editor->begin_reversible_command ("xfade trim");
6882 ar->playlist()->clear_owned_changes ();
6885 ar->set_fade_in_length (new_length);
6887 ar->set_fade_out_length (new_length);
6890 /* Adjusting the xfade may affect other regions in the playlist, so we need
6891 to get undo Commands from the whole playlist rather than just the
6895 vector<Command*> cmds;
6896 ar->playlist()->rdiff (cmds);
6897 _editor->session()->add_commands (cmds);
6898 _editor->commit_reversible_command ();
6903 CrossfadeEdgeDrag::aborted (bool)
6906 // arv->redraw_start_xfade ();
6908 // arv->redraw_end_xfade ();
6912 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6913 : Drag (e, item, true)
6914 , line (new EditorCursor (*e))
6916 line->set_position (pos);
6918 line->track_canvas_item().reparent (_editor->_drag_motion_group);
6921 RegionCutDrag::~RegionCutDrag ()
6927 RegionCutDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6929 Drag::start_grab (event, c);
6930 motion (event, false);
6934 RegionCutDrag::motion (GdkEvent* event, bool)
6936 MusicFrame pos (_drags->current_pointer_frame(), 0);
6937 _editor->snap_to_with_modifier (pos, event);
6939 line->set_position (pos.frame);
6943 RegionCutDrag::finished (GdkEvent* event, bool)
6945 _editor->get_track_canvas()->canvas()->re_enter();
6948 MusicFrame pos (_drags->current_pointer_frame(), 0);
6949 _editor->snap_to_with_modifier (pos, event);
6952 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos.frame);
6958 _editor->split_regions_at (pos, rs, false);
6962 RegionCutDrag::aborted (bool)
6966 RulerZoomDrag::RulerZoomDrag (Editor* e, ArdourCanvas::Item* item)
6967 : Drag (e, item, true)
6972 RulerZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6974 Drag::start_grab (event, c);
6976 framepos_t where = _editor->canvas_event_sample(event);
6978 _editor->_dragging_playhead = true;
6980 _editor->playhead_cursor->set_position (where);
6984 RulerZoomDrag::motion (GdkEvent* event, bool)
6986 framepos_t where = _editor->canvas_event_sample(event);
6988 _editor->playhead_cursor->set_position (where);
6990 const double movement_limit = 20.0;
6991 const double scale = 1.08;
6992 const double y_delta = last_pointer_y() - current_pointer_y();
6994 if (y_delta > 0 && y_delta < movement_limit) {
6995 _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
6996 } else if (y_delta < 0 && y_delta > -movement_limit) {
6997 _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
7002 RulerZoomDrag::finished (GdkEvent*, bool)
7004 _editor->_dragging_playhead = false;
7006 Session* s = _editor->session ();
7008 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
7009 _editor->_pending_locate_request = true;
7015 RulerZoomDrag::aborted (bool)