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 TimeAxisViewStripableSorter {
550 bool operator() (TimeAxisView* tav_a, TimeAxisView* tav_b) {
551 boost::shared_ptr<ARDOUR::Stripable> const& a = tav_a->stripable ();
552 boost::shared_ptr<ARDOUR::Stripable> const& b = tav_b->stripable ();
553 return ARDOUR::Stripable::Sorter () (a, b);
557 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
562 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
564 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
565 as some of the regions we are dragging may be on such tracks.
568 TrackViewList track_views = _editor->track_views;
569 track_views.sort (TimeAxisViewStripableSorter ());
571 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
572 _time_axis_views.push_back (*i);
574 TimeAxisView::Children children_list = (*i)->get_child_list ();
575 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
576 _time_axis_views.push_back (j->get());
580 /* the list of views can be empty at this point if this is a region list-insert drag
583 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
584 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
587 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
591 RegionDrag::region_going_away (RegionView* v)
593 list<DraggingView>::iterator i = _views.begin ();
594 while (i != _views.end() && i->view != v) {
598 if (i != _views.end()) {
603 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
604 * or -1 if it is not found.
607 RegionDrag::find_time_axis_view (TimeAxisView* t) const
610 int const N = _time_axis_views.size ();
611 while (i < N && _time_axis_views[i] != t) {
615 if (_time_axis_views[i] != t) {
622 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
623 : RegionDrag (e, i, p, v)
625 , _ignore_video_lock (false)
626 , _last_position (0, 0)
628 , _last_pointer_time_axis_view (0)
629 , _last_pointer_layer (0)
634 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
638 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
640 Drag::start_grab (event, cursor);
641 setup_snap_delta (_last_position);
643 show_verbose_cursor_time (_last_position.frame);
645 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
647 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
648 assert(_last_pointer_time_axis_view >= 0);
649 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
652 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
653 _ignore_video_lock = true;
657 /* cross track dragging seems broken here. disabled for now. */
658 _y_constrained = true;
663 RegionMotionDrag::compute_x_delta (GdkEvent const * event, MusicFrame* pending_region_position)
665 /* compute the amount of pointer motion in frames, and where
666 the region would be if we moved it by that much.
668 if (_x_constrained) {
669 *pending_region_position = _last_position;
673 *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
675 framecnt_t sync_offset;
678 sync_offset = _primary->region()->sync_offset (sync_dir);
680 /* we don't handle a sync point that lies before zero.
682 if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position->frame >= sync_offset)) {
684 framecnt_t const sd = snap_delta (event->button.state);
685 MusicFrame sync_snap (pending_region_position->frame + (sync_dir * sync_offset) + sd, 0);
686 _editor->snap_to_with_modifier (sync_snap, event);
687 if (sync_offset == 0 && sd == 0) {
688 *pending_region_position = sync_snap;
690 pending_region_position->set (_primary->region()->adjust_to_sync (sync_snap.frame) - sd, 0);
693 *pending_region_position = _last_position;
696 if (pending_region_position->frame > max_framepos - _primary->region()->length()) {
697 *pending_region_position = _last_position;
702 bool const x_move_allowed = !_x_constrained;
704 if ((pending_region_position->frame != _last_position.frame) && x_move_allowed) {
706 /* x movement since last time (in pixels) */
707 dx = _editor->sample_to_pixel_unrounded (pending_region_position->frame - _last_position.frame);
709 /* total x movement */
710 framecnt_t total_dx = _editor->pixel_to_sample (_total_x_delta + dx);
712 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
713 frameoffset_t const off = i->view->region()->position() + total_dx;
715 dx = dx - _editor->sample_to_pixel_unrounded (off);
716 *pending_region_position = MusicFrame (pending_region_position->frame - off, 0);
726 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
732 const int tavsize = _time_axis_views.size();
733 const int dt = delta > 0 ? +1 : -1;
735 int target = start + delta - skip;
737 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
738 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
740 while (current >= 0 && current != target) {
742 if (current < 0 && dt < 0) {
745 if (current >= tavsize && dt > 0) {
748 if (current < 0 || current >= tavsize) {
752 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
753 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
757 if (distance_only && current == start + delta) {
765 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
767 if (_y_constrained) {
771 const int tavsize = _time_axis_views.size();
772 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
773 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
774 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
776 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
777 /* already in the drop zone */
778 if (delta_track >= 0) {
779 /* downward motion - OK if others are still not in the dropzone */
788 } else if (n >= tavsize) {
789 /* downward motion into drop zone. That's fine. */
793 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
794 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
795 /* not a track, or the wrong type */
799 double const l = i->layer + delta_layer;
801 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
802 mode to allow the user to place a region below another on layer 0.
804 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
805 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
806 If it has, the layers will be munged later anyway, so it's ok.
812 /* all regions being dragged are ok with this change */
816 struct DraggingViewSorter {
817 bool operator() (const DraggingView& a, const DraggingView& b) {
818 return a.time_axis_view < b.time_axis_view;
823 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
825 double delta_layer = 0;
826 int delta_time_axis_view = 0;
827 int current_pointer_time_axis_view = -1;
829 assert (!_views.empty ());
831 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
833 /* Find the TimeAxisView that the pointer is now over */
834 const double cur_y = current_pointer_y ();
835 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
836 TimeAxisView* tv = r.first;
838 if (!tv && cur_y < 0) {
839 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
843 /* find drop-zone y-position */
844 Coord last_track_bottom_edge;
845 last_track_bottom_edge = 0;
846 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
847 if (!(*t)->hidden()) {
848 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
853 if (tv && tv->view()) {
854 /* the mouse is over a track */
855 double layer = r.second;
857 if (first_move && tv->view()->layer_display() == Stacked) {
858 tv->view()->set_layer_display (Expanded);
861 /* Here's the current pointer position in terms of time axis view and layer */
862 current_pointer_time_axis_view = find_time_axis_view (tv);
863 assert(current_pointer_time_axis_view >= 0);
865 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
867 /* Work out the change in y */
869 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
870 if (!rtv || !rtv->is_track()) {
871 /* ignore non-tracks early on. we can't move any regions on them */
872 } else if (_last_pointer_time_axis_view < 0) {
873 /* Was in the drop-zone, now over a track.
874 * Hence it must be an upward move (from the bottom)
876 * track_index is still -1, so delta must be set to
877 * move up the correct number of tracks from the bottom.
879 * This is necessary because steps may be skipped if
880 * the bottom-most track is not a valid target and/or
881 * if there are hidden tracks at the bottom.
882 * Hence the initial offset (_ddropzone) as well as the
883 * last valid pointer position (_pdropzone) need to be
884 * taken into account.
886 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
888 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
891 /* TODO needs adjustment per DraggingView,
893 * e.g. select one region on the top-layer of a track
894 * and one region which is at the bottom-layer of another track
897 * Indicated drop-zones and layering is wrong.
898 * and may infer additional layers on the target-track
899 * (depending how many layers the original track had).
901 * Or select two regions (different layers) on a same track,
902 * move across a non-layer track.. -> layering info is lost.
903 * on drop either of the regions may be on top.
905 * Proposed solution: screw it :) well,
906 * don't use delta_layer, use an absolute value
907 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
908 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
909 * 3) iterate over all DraggingView, find the one that is over the track with most layers
910 * 4) proportionally scale layer to layers available on target
912 delta_layer = current_pointer_layer - _last_pointer_layer;
915 /* for automation lanes, there is a TimeAxisView but no ->view()
916 * if (!tv) -> dropzone
918 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
919 /* Moving into the drop-zone.. */
920 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
921 /* delta_time_axis_view may not be sufficient to move into the DZ
922 * the mouse may enter it, but it may not be a valid move due to
925 * -> remember the delta needed to move into the dropzone
927 _ddropzone = delta_time_axis_view;
928 /* ..but subtract hidden tracks (or routes) at the bottom.
929 * we silently move mover them
931 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
932 - _time_axis_views.size();
934 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
935 /* move around inside the zone.
936 * This allows to move further down until all regions are in the zone.
938 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
939 assert(ptr_y >= last_track_bottom_edge);
940 assert(_ddropzone > 0);
942 /* calculate mouse position in 'tracks' below last track. */
943 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
944 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
946 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
948 delta_time_axis_view = dzpos - _pdropzone;
949 } else if (dzpos < _pdropzone && _ndropzone > 0) {
950 // move up inside the DZ
951 delta_time_axis_view = dzpos - _pdropzone;
955 /* Work out the change in x */
956 TempoMap& tmap = _editor->session()->tempo_map();
957 MusicFrame pending_region_position (0, 0);
958 double const x_delta = compute_x_delta (event, &pending_region_position);
960 double const last_pos_qn = tmap.exact_qn_at_frame (_last_position.frame, _last_position.division);
961 double const qn_delta = tmap.exact_qn_at_frame (pending_region_position.frame, pending_region_position.division) - last_pos_qn;
963 _last_position = pending_region_position;
965 /* calculate hidden tracks in current y-axis delta */
967 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
968 /* The mouse is more than one track below the dropzone.
969 * distance calculation is not needed (and would not work, either
970 * because the dropzone is "packed").
972 * Except when [partially] moving regions out of dropzone in a large step.
973 * (the mouse may or may not remain in the DZ)
974 * Hidden tracks at the bottom of the TAV need to be skipped.
976 * This also handles the case if the mouse entered the DZ
977 * in a large step (exessive delta), either due to fast-movement,
978 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
980 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
981 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
983 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
984 -_time_axis_views.size() - dt;
987 else if (_last_pointer_time_axis_view < 0) {
988 /* Moving out of the zone. Check for hidden tracks at the bottom. */
989 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
990 -_time_axis_views.size() - delta_time_axis_view;
992 /* calculate hidden tracks that are skipped by the pointer movement */
993 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
994 - _last_pointer_time_axis_view
995 - delta_time_axis_view;
998 /* Verify change in y */
999 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
1000 /* this y movement is not allowed, so do no y movement this time */
1001 delta_time_axis_view = 0;
1006 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
1007 /* haven't reached next snap point, and we're not switching
1008 trackviews nor layers. nothing to do.
1013 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
1014 PlaylistDropzoneMap playlist_dropzone_map;
1015 _ndropzone = 0; // number of elements currently in the dropzone
1018 /* sort views by time_axis.
1019 * This retains track order in the dropzone, regardless
1020 * of actual selection order
1022 _views.sort (DraggingViewSorter());
1024 /* count number of distinct tracks of all regions
1025 * being dragged, used for dropzone.
1027 int prev_track = -1;
1028 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1029 if (i->time_axis_view != prev_track) {
1030 prev_track = i->time_axis_view;
1036 _views.back().time_axis_view -
1037 _views.front().time_axis_view;
1039 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1040 - _views.back().time_axis_view;
1042 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1046 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1048 RegionView* rv = i->view;
1053 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1060 /* reparent the regionview into a group above all
1064 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1065 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1066 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1067 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1068 /* move the item so that it continues to appear at the
1069 same location now that its parent has changed.
1071 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1074 /* If we have moved tracks, we'll fudge the layer delta so that the
1075 region gets moved back onto layer 0 on its new track; this avoids
1076 confusion when dragging regions from non-zero layers onto different
1079 double this_delta_layer = delta_layer;
1080 if (delta_time_axis_view != 0) {
1081 this_delta_layer = - i->layer;
1084 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1086 int track_index = i->time_axis_view + this_delta_time_axis_view;
1087 assert(track_index >= 0);
1089 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1090 /* Track is in the Dropzone */
1092 i->time_axis_view = track_index;
1093 assert(i->time_axis_view >= (int) _time_axis_views.size());
1096 double yposition = 0;
1097 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1098 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1101 /* store index of each new playlist as a negative count, starting at -1 */
1103 if (pdz == playlist_dropzone_map.end()) {
1104 /* compute where this new track (which doesn't exist yet) will live
1107 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1109 /* How high is this region view ? */
1111 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1112 ArdourCanvas::Rect bbox;
1115 bbox = obbox.get ();
1118 last_track_bottom_edge += bbox.height();
1120 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1123 yposition = pdz->second;
1126 /* values are zero or negative, hence the use of min() */
1127 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1130 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1132 mrv->apply_note_range (60, 71, true);
1136 /* The TimeAxisView that this region is now over */
1137 TimeAxisView* current_tv = _time_axis_views[track_index];
1139 /* Ensure it is moved from stacked -> expanded if appropriate */
1140 if (current_tv->view()->layer_display() == Stacked) {
1141 current_tv->view()->set_layer_display (Expanded);
1144 /* We're only allowed to go -ve in layer on Expanded views */
1145 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1146 this_delta_layer = - i->layer;
1150 rv->set_height (current_tv->view()->child_height ());
1152 /* Update show/hidden status as the region view may have come from a hidden track,
1153 or have moved to one.
1155 if (current_tv->hidden ()) {
1156 rv->get_canvas_group()->hide ();
1158 rv->get_canvas_group()->show ();
1161 /* Update the DraggingView */
1162 i->time_axis_view = track_index;
1163 i->layer += this_delta_layer;
1166 _editor->mouse_brush_insert_region (rv, pending_region_position.frame);
1170 /* Get the y coordinate of the top of the track that this region is now over */
1171 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1173 /* And adjust for the layer that it should be on */
1174 StreamView* cv = current_tv->view ();
1175 switch (cv->layer_display ()) {
1179 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1182 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1186 /* need to get the parent of the regionview
1187 * canvas group and get its position in
1188 * equivalent coordinate space as the trackview
1189 * we are now dragging over.
1192 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1196 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1198 MidiStreamView* msv;
1199 if ((msv = dynamic_cast <MidiStreamView*> (current_tv->view())) != 0) {
1200 mrv->apply_note_range (msv->lowest_note(), msv->highest_note(), true);
1205 /* Now move the region view */
1206 if (rv->region()->position_lock_style() == MusicTime) {
1207 double const last_qn = tmap.quarter_note_at_frame (rv->get_position());
1208 framepos_t const x_pos_music = tmap.frame_at_quarter_note (last_qn + qn_delta);
1210 rv->set_position (x_pos_music, 0);
1211 rv->move (0, y_delta);
1213 rv->move (x_delta, y_delta);
1216 } /* foreach region */
1218 _total_x_delta += x_delta;
1220 if (x_delta != 0 && !_brushing) {
1221 show_verbose_cursor_time (_last_position.frame);
1224 /* keep track of pointer movement */
1226 /* the pointer is currently over a time axis view */
1228 if (_last_pointer_time_axis_view < 0) {
1229 /* last motion event was not over a time axis view
1230 * or last y-movement out of the dropzone was not valid
1233 if (delta_time_axis_view < 0) {
1234 /* in the drop zone, moving up */
1236 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1237 * We do not use negative _last_pointer_time_axis_view because
1238 * the dropzone is "packed" (the actual track offset is ignored)
1240 * As opposed to the actual number
1241 * of elements in the dropzone (_ndropzone)
1242 * _pdropzone is not constrained. This is necessary
1243 * to allow moving multiple regions with y-distance
1246 * There can be 0 elements in the dropzone,
1247 * even though the drag-pointer is inside the DZ.
1250 * [ Audio-track, Midi-track, Audio-track, DZ ]
1251 * move regions from both audio tracks at the same time into the
1252 * DZ by grabbing the region in the bottom track.
1254 assert(current_pointer_time_axis_view >= 0);
1255 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1259 /* only move out of the zone if the movement is OK */
1260 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1261 assert(delta_time_axis_view < 0);
1262 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1263 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1264 * the current position can be calculated as follows:
1266 // a well placed oofus attack can still throw this off.
1267 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1268 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1271 /* last motion event was also over a time axis view */
1272 _last_pointer_time_axis_view += delta_time_axis_view;
1273 assert(_last_pointer_time_axis_view >= 0);
1278 /* the pointer is not over a time axis view */
1279 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1280 _pdropzone += delta_time_axis_view - delta_skip;
1281 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1284 _last_pointer_layer += delta_layer;
1288 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1290 if (_copy && first_move) {
1291 if (_x_constrained && !_brushing) {
1292 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1293 } else if (!_brushing) {
1294 _editor->begin_reversible_command (Operations::region_copy);
1295 } else if (_brushing) {
1296 _editor->begin_reversible_command (Operations::drag_region_brush);
1298 /* duplicate the regionview(s) and region(s) */
1300 list<DraggingView> new_regionviews;
1302 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1304 RegionView* rv = i->view;
1305 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1306 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1308 const boost::shared_ptr<const Region> original = rv->region();
1309 boost::shared_ptr<Region> region_copy;
1311 region_copy = RegionFactory::create (original, true);
1313 /* need to set this so that the drop zone code can work. This doesn't
1314 actually put the region into the playlist, but just sets a weak pointer
1317 region_copy->set_playlist (original->playlist());
1321 boost::shared_ptr<AudioRegion> audioregion_copy
1322 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1324 nrv = new AudioRegionView (*arv, audioregion_copy);
1326 boost::shared_ptr<MidiRegion> midiregion_copy
1327 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1328 nrv = new MidiRegionView (*mrv, midiregion_copy);
1333 nrv->get_canvas_group()->show ();
1334 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1336 /* swap _primary to the copy */
1338 if (rv == _primary) {
1342 /* ..and deselect the one we copied */
1344 rv->set_selected (false);
1347 if (!new_regionviews.empty()) {
1349 /* reflect the fact that we are dragging the copies */
1351 _views = new_regionviews;
1353 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1356 } else if (!_copy && first_move) {
1357 if (_x_constrained && !_brushing) {
1358 _editor->begin_reversible_command (_("fixed time region drag"));
1359 } else if (!_brushing) {
1360 _editor->begin_reversible_command (Operations::region_drag);
1361 } else if (_brushing) {
1362 _editor->begin_reversible_command (Operations::drag_region_brush);
1365 RegionMotionDrag::motion (event, first_move);
1369 RegionMotionDrag::finished (GdkEvent *, bool)
1371 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1372 if (!(*i)->view()) {
1376 if ((*i)->view()->layer_display() == Expanded) {
1377 (*i)->view()->set_layer_display (Stacked);
1383 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1385 RegionMotionDrag::finished (ev, movement_occurred);
1387 if (!movement_occurred) {
1391 if (was_double_click() && !_views.empty()) {
1392 DraggingView dv = _views.front();
1393 _editor->edit_region (dv.view);
1399 assert (!_views.empty ());
1401 /* We might have hidden region views so that they weren't visible during the drag
1402 (when they have been reparented). Now everything can be shown again, as region
1403 views are back in their track parent groups.
1405 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1406 i->view->get_canvas_group()->show ();
1409 bool const changed_position = (_last_position.frame != _primary->region()->position());
1410 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1434 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1436 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1439 TimeAxisView* tav = 0;
1441 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1442 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1443 uint32_t output_chan = region->n_channels();
1444 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1445 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1447 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
1448 tav =_editor->time_axis_view_from_stripable (audio_tracks.front());
1450 ChanCount one_midi_port (DataType::MIDI, 1);
1451 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1452 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port,
1453 Config->get_strict_io () || Profile->get_mixbus (),
1454 boost::shared_ptr<ARDOUR::PluginInfo>(),
1455 (ARDOUR::Plugin::PresetRecord*) 0,
1456 (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
1457 tav = _editor->time_axis_view_from_stripable (midi_tracks.front());
1461 tav->set_height (original->current_height());
1464 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1467 return dynamic_cast<RouteTimeAxisView*> (tav);
1471 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, MusicFrame last_position, int32_t const ev_state)
1473 RegionSelection new_views;
1474 PlaylistSet modified_playlists;
1475 RouteTimeAxisView* new_time_axis_view = 0;
1476 framecnt_t const drag_delta = _primary->region()->position() - _last_position.frame;
1478 TempoMap& tmap (_editor->session()->tempo_map());
1479 const double last_pos_qn = tmap.exact_qn_at_frame (last_position.frame, last_position.division);
1480 const double qn_delta = _primary->region()->quarter_note() - last_pos_qn;
1483 /* all changes were made during motion event handlers */
1485 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1489 _editor->commit_reversible_command ();
1493 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1494 PlaylistMapping playlist_mapping;
1496 /* insert the regions into their new playlists */
1497 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1499 RouteTimeAxisView* dest_rtv = 0;
1501 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1505 MusicFrame where (0, 0);
1506 double quarter_note;
1508 if (changed_position && !_x_constrained) {
1509 where.set (i->view->region()->position() - drag_delta, 0);
1510 quarter_note = i->view->region()->quarter_note() - qn_delta;
1512 /* region has not moved - divisor will not affect musical pos */
1513 where.set (i->view->region()->position(), 0);
1514 quarter_note = i->view->region()->quarter_note();
1517 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1518 /* dragged to drop zone */
1520 PlaylistMapping::iterator pm;
1522 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1523 /* first region from this original playlist: create a new track */
1524 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1525 if(!new_time_axis_view) {
1529 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1530 dest_rtv = new_time_axis_view;
1532 /* we already created a new track for regions from this playlist, use it */
1533 dest_rtv = pm->second;
1536 /* destination time axis view is the one we dragged to */
1537 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1540 if (dest_rtv != 0) {
1541 RegionView* new_view;
1542 if (i->view == _primary && !_x_constrained) {
1543 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, last_position, last_pos_qn,
1544 modified_playlists, true);
1546 if (i->view->region()->position_lock_style() == AudioTime) {
1547 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, quarter_note,
1548 modified_playlists);
1550 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, quarter_note,
1551 modified_playlists, true);
1555 if (new_view != 0) {
1556 new_views.push_back (new_view);
1560 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1561 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1564 list<DraggingView>::const_iterator next = i;
1570 /* If we've created new regions either by copying or moving
1571 to a new track, we want to replace the old selection with the new ones
1574 if (new_views.size() > 0) {
1575 _editor->selection->set (new_views);
1578 /* write commands for the accumulated diffs for all our modified playlists */
1579 add_stateful_diff_commands_for_playlists (modified_playlists);
1581 _editor->commit_reversible_command ();
1585 RegionMoveDrag::finished_no_copy (
1586 bool const changed_position,
1587 bool const changed_tracks,
1588 MusicFrame last_position,
1589 int32_t const ev_state
1592 RegionSelection new_views;
1593 PlaylistSet modified_playlists;
1594 PlaylistSet frozen_playlists;
1595 set<RouteTimeAxisView*> views_to_update;
1596 RouteTimeAxisView* new_time_axis_view = 0;
1597 framecnt_t const drag_delta = _primary->region()->position() - last_position.frame;
1599 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1600 PlaylistMapping playlist_mapping;
1602 TempoMap& tmap (_editor->session()->tempo_map());
1603 const double last_pos_qn = tmap.exact_qn_at_frame (last_position.frame, last_position.division);
1604 const double qn_delta = _primary->region()->quarter_note() - last_pos_qn;
1606 std::set<boost::shared_ptr<const Region> > uniq;
1607 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1609 RegionView* rv = i->view;
1610 RouteTimeAxisView* dest_rtv = 0;
1612 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1617 if (uniq.find (rv->region()) != uniq.end()) {
1618 /* prevent duplicate moves when selecting regions from shared playlists */
1622 uniq.insert(rv->region());
1624 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1625 /* dragged to drop zone */
1627 PlaylistMapping::iterator pm;
1629 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1630 /* first region from this original playlist: create a new track */
1631 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1632 if(!new_time_axis_view) { // New track creation failed
1636 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1637 dest_rtv = new_time_axis_view;
1639 /* we already created a new track for regions from this playlist, use it */
1640 dest_rtv = pm->second;
1644 /* destination time axis view is the one we dragged to */
1645 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1650 double const dest_layer = i->layer;
1652 views_to_update.insert (dest_rtv);
1654 MusicFrame where (0, 0);
1655 double quarter_note;
1657 if (changed_position && !_x_constrained) {
1658 where.set (rv->region()->position() - drag_delta, 0);
1659 quarter_note = i->view->region()->quarter_note() - qn_delta;
1661 where.set (rv->region()->position(), 0);
1662 quarter_note = i->view->region()->quarter_note();
1665 if (changed_tracks) {
1667 /* insert into new playlist */
1668 RegionView* new_view;
1669 if (rv == _primary && !_x_constrained) {
1670 new_view = insert_region_into_playlist (
1671 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, last_position, last_pos_qn,
1672 modified_playlists, true
1675 if (rv->region()->position_lock_style() == AudioTime) {
1677 new_view = insert_region_into_playlist (
1678 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, quarter_note,
1682 new_view = insert_region_into_playlist (
1683 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, quarter_note,
1684 modified_playlists, true
1689 if (new_view == 0) {
1694 new_views.push_back (new_view);
1696 /* remove from old playlist */
1698 /* the region that used to be in the old playlist is not
1699 moved to the new one - we use a copy of it. as a result,
1700 any existing editor for the region should no longer be
1703 rv->hide_region_editor();
1706 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1710 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1712 /* this movement may result in a crossfade being modified, or a layering change,
1713 so we need to get undo data from the playlist as well as the region.
1716 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1718 playlist->clear_changes ();
1721 rv->region()->clear_changes ();
1724 motion on the same track. plonk the previously reparented region
1725 back to its original canvas group (its streamview).
1726 No need to do anything for copies as they are fake regions which will be deleted.
1729 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1730 rv->get_canvas_group()->set_y_position (i->initial_y);
1733 /* just change the model */
1734 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1735 playlist->set_layer (rv->region(), dest_layer);
1738 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1740 r = frozen_playlists.insert (playlist);
1743 playlist->freeze ();
1745 if (rv == _primary) {
1746 rv->region()->set_position (where.frame, last_position.division);
1748 if (rv->region()->position_lock_style() == AudioTime) {
1749 /* move by frame offset */
1750 rv->region()->set_position (where.frame, 0);
1752 /* move by music offset */
1753 rv->region()->set_position_music (rv->region()->quarter_note() - qn_delta);
1756 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1759 if (changed_tracks) {
1761 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1762 was selected in all of them, then removing it from a playlist will have removed all
1763 trace of it from _views (i.e. there were N regions selected, we removed 1,
1764 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1765 corresponding regionview, and _views is now empty).
1767 This could have invalidated any and all iterators into _views.
1769 The heuristic we use here is: if the region selection is empty, break out of the loop
1770 here. if the region selection is not empty, then restart the loop because we know that
1771 we must have removed at least the region(view) we've just been working on as well as any
1772 that we processed on previous iterations.
1774 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1775 we can just iterate.
1779 if (_views.empty()) {
1790 /* If we've created new regions either by copying or moving
1791 to a new track, we want to replace the old selection with the new ones
1794 if (new_views.size() > 0) {
1795 _editor->selection->set (new_views);
1798 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1802 /* write commands for the accumulated diffs for all our modified playlists */
1803 add_stateful_diff_commands_for_playlists (modified_playlists);
1804 /* applies to _brushing */
1805 _editor->commit_reversible_command ();
1807 /* We have futzed with the layering of canvas items on our streamviews.
1808 If any region changed layer, this will have resulted in the stream
1809 views being asked to set up their region views, and all will be well.
1810 If not, we might now have badly-ordered region views. Ask the StreamViews
1811 involved to sort themselves out, just in case.
1814 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1815 (*i)->view()->playlist_layered ((*i)->track ());
1819 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1820 * @param region Region to remove.
1821 * @param playlist playlist To remove from.
1822 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1823 * that clear_changes () is only called once per playlist.
1826 RegionMoveDrag::remove_region_from_playlist (
1827 boost::shared_ptr<Region> region,
1828 boost::shared_ptr<Playlist> playlist,
1829 PlaylistSet& modified_playlists
1832 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1835 playlist->clear_changes ();
1838 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1842 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1843 * clearing the playlist's diff history first if necessary.
1844 * @param region Region to insert.
1845 * @param dest_rtv Destination RouteTimeAxisView.
1846 * @param dest_layer Destination layer.
1847 * @param where Destination position.
1848 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1849 * that clear_changes () is only called once per playlist.
1850 * @return New RegionView, or 0 if no insert was performed.
1853 RegionMoveDrag::insert_region_into_playlist (
1854 boost::shared_ptr<Region> region,
1855 RouteTimeAxisView* dest_rtv,
1858 double quarter_note,
1859 PlaylistSet& modified_playlists,
1863 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1864 if (!dest_playlist) {
1868 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1869 _new_region_view = 0;
1870 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1872 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1873 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1875 dest_playlist->clear_changes ();
1878 dest_playlist->add_region (region, where.frame, 1.0, false, where.division, quarter_note, true);
1880 dest_playlist->add_region (region, where.frame, 1.0, false, where.division);
1883 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1884 dest_playlist->set_layer (region, dest_layer);
1889 assert (_new_region_view);
1891 return _new_region_view;
1895 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1897 _new_region_view = rv;
1901 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1903 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1904 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1906 _editor->session()->add_command (c);
1915 RegionMoveDrag::aborted (bool movement_occurred)
1919 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1920 list<DraggingView>::const_iterator next = i;
1929 RegionMotionDrag::aborted (movement_occurred);
1934 RegionMotionDrag::aborted (bool)
1936 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1938 StreamView* sview = (*i)->view();
1941 if (sview->layer_display() == Expanded) {
1942 sview->set_layer_display (Stacked);
1947 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1948 RegionView* rv = i->view;
1949 TimeAxisView* tv = &(rv->get_time_axis_view ());
1950 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1952 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1953 rv->get_canvas_group()->set_y_position (0);
1955 rv->move (-_total_x_delta, 0);
1956 rv->set_height (rtv->view()->child_height ());
1960 /** @param b true to brush, otherwise false.
1961 * @param c true to make copies of the regions being moved, otherwise false.
1963 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1964 : RegionMotionDrag (e, i, p, v, b)
1966 , _new_region_view (0)
1968 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1971 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1972 if (rtv && rtv->is_track()) {
1973 speed = rtv->track()->speed ();
1976 _last_position = MusicFrame (static_cast<framepos_t> (_primary->region()->position() / speed), 0);
1980 RegionMoveDrag::setup_pointer_frame_offset ()
1982 _pointer_frame_offset = raw_grab_frame() - _last_position.frame;
1985 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1986 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1988 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1990 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1991 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1993 _primary = v->view()->create_region_view (r, false, false);
1995 _primary->get_canvas_group()->show ();
1996 _primary->set_position (pos, 0);
1997 _views.push_back (DraggingView (_primary, this, v));
1999 _last_position = MusicFrame (pos, 0);
2001 _item = _primary->get_canvas_group ();
2005 RegionInsertDrag::finished (GdkEvent * event, bool)
2007 int pos = _views.front().time_axis_view;
2008 assert(pos >= 0 && pos < (int)_time_axis_views.size());
2010 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
2012 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
2013 _primary->get_canvas_group()->set_y_position (0);
2015 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
2017 _editor->begin_reversible_command (Operations::insert_region);
2018 playlist->clear_changes ();
2019 _editor->snap_to_with_modifier (_last_position, event);
2021 playlist->add_region (_primary->region (), _last_position.frame, 1.0, false, _last_position.division);
2023 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
2024 if (Config->get_edit_mode() == Ripple) {
2025 playlist->ripple (_last_position.frame, _primary->region()->length(), _primary->region());
2028 _editor->session()->add_command (new StatefulDiffCommand (playlist));
2029 _editor->commit_reversible_command ();
2037 RegionInsertDrag::aborted (bool)
2044 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2045 : RegionMoveDrag (e, i, p, v, false, false)
2047 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
2050 struct RegionSelectionByPosition {
2051 bool operator() (RegionView*a, RegionView* b) {
2052 return a->region()->position () < b->region()->position();
2057 RegionSpliceDrag::motion (GdkEvent* event, bool)
2059 /* Which trackview is this ? */
2061 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2062 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2064 /* The region motion is only processed if the pointer is over
2068 if (!tv || !tv->is_track()) {
2069 /* To make sure we hide the verbose canvas cursor when the mouse is
2070 not held over an audio track.
2072 _editor->verbose_cursor()->hide ();
2075 _editor->verbose_cursor()->show ();
2080 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
2086 RegionSelection copy;
2087 _editor->selection->regions.by_position(copy);
2089 framepos_t const pf = adjusted_current_frame (event);
2091 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2093 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
2099 boost::shared_ptr<Playlist> playlist;
2101 if ((playlist = atv->playlist()) == 0) {
2105 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
2110 if (pf < (*i)->region()->last_frame() + 1) {
2114 if (pf > (*i)->region()->first_frame()) {
2120 playlist->shuffle ((*i)->region(), dir);
2125 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2127 RegionMoveDrag::finished (event, movement_occurred);
2131 RegionSpliceDrag::aborted (bool)
2141 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2144 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2146 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2147 RegionSelection to_ripple;
2148 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2149 if ((*i)->position() >= where) {
2150 to_ripple.push_back (rtv->view()->find_view(*i));
2154 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2155 if (!exclude.contains (*i)) {
2156 // the selection has already been added to _views
2158 if (drag_in_progress) {
2159 // do the same things that RegionMotionDrag::motion does when
2160 // first_move is true, for the region views that we're adding
2161 // to _views this time
2164 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2165 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2166 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2167 rvg->reparent (_editor->_drag_motion_group);
2169 // we only need to move in the y direction
2170 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2175 _views.push_back (DraggingView (*i, this, tav));
2181 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2184 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2185 // we added all the regions after the selection
2187 std::list<DraggingView>::iterator to_erase = i++;
2188 if (!_editor->selection->regions.contains (to_erase->view)) {
2189 // restore the non-selected regions to their original playlist & positions,
2190 // and then ripple them back by the length of the regions that were dragged away
2191 // do the same things as RegionMotionDrag::aborted
2193 RegionView *rv = to_erase->view;
2194 TimeAxisView* tv = &(rv->get_time_axis_view ());
2195 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2198 // plonk them back onto their own track
2199 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2200 rv->get_canvas_group()->set_y_position (0);
2204 // move the underlying region to match the view
2205 rv->region()->set_position (rv->region()->position() + amount);
2207 // restore the view to match the underlying region's original position
2208 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2211 rv->set_height (rtv->view()->child_height ());
2212 _views.erase (to_erase);
2218 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2220 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2222 return allow_moves_across_tracks;
2230 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2231 : RegionMoveDrag (e, i, p, v, false, false)
2233 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2234 // compute length of selection
2235 RegionSelection selected_regions = _editor->selection->regions;
2236 selection_length = selected_regions.end_frame() - selected_regions.start();
2238 // we'll only allow dragging to another track in ripple mode if all the regions
2239 // being dragged start off on the same track
2240 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2243 exclude = new RegionList;
2244 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2245 exclude->push_back((*i)->region());
2248 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2249 RegionSelection copy;
2250 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2252 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2253 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2255 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2256 // find ripple start point on each applicable playlist
2257 RegionView *first_selected_on_this_track = NULL;
2258 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2259 if ((*i)->region()->playlist() == (*pi)) {
2260 // region is on this playlist - it's the first, because they're sorted
2261 first_selected_on_this_track = *i;
2265 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2266 add_all_after_to_views (
2267 &first_selected_on_this_track->get_time_axis_view(),
2268 first_selected_on_this_track->region()->position(),
2269 selected_regions, false);
2272 if (allow_moves_across_tracks) {
2273 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2281 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2283 /* Which trackview is this ? */
2285 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2286 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2288 /* The region motion is only processed if the pointer is over
2292 if (!tv || !tv->is_track()) {
2293 /* To make sure we hide the verbose canvas cursor when the mouse is
2294 not held over an audiotrack.
2296 _editor->verbose_cursor()->hide ();
2300 framepos_t where = adjusted_current_frame (event);
2301 assert (where >= 0);
2302 MusicFrame after (0, 0);
2303 double delta = compute_x_delta (event, &after);
2305 framecnt_t amount = _editor->pixel_to_sample (delta);
2307 if (allow_moves_across_tracks) {
2308 // all the originally selected regions were on the same track
2310 framecnt_t adjust = 0;
2311 if (prev_tav && tv != prev_tav) {
2312 // dragged onto a different track
2313 // remove the unselected regions from _views, restore them to their original positions
2314 // and add the regions after the drop point on the new playlist to _views instead.
2315 // undo the effect of rippling the previous playlist, and include the effect of removing
2316 // the dragged region(s) from this track
2318 remove_unselected_from_views (prev_amount, false);
2319 // ripple previous playlist according to the regions that have been removed onto the new playlist
2320 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2323 // move just the selected regions
2324 RegionMoveDrag::motion(event, first_move);
2326 // ensure that the ripple operation on the new playlist inserts selection_length time
2327 adjust = selection_length;
2328 // ripple the new current playlist
2329 tv->playlist()->ripple (where, amount+adjust, exclude);
2331 // add regions after point where drag entered this track to subsequent ripples
2332 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2335 // motion on same track
2336 RegionMoveDrag::motion(event, first_move);
2340 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2341 prev_position = where;
2343 // selection encompasses multiple tracks - just drag
2344 // cross-track drags are forbidden
2345 RegionMoveDrag::motion(event, first_move);
2348 if (!_x_constrained) {
2349 prev_amount += amount;
2352 _last_position = after;
2356 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2358 if (!movement_occurred) {
2362 if (was_double_click() && !_views.empty()) {
2363 DraggingView dv = _views.front();
2364 _editor->edit_region (dv.view);
2370 _editor->begin_reversible_command(_("Ripple drag"));
2372 // remove the regions being rippled from the dragging view, updating them to
2373 // their new positions
2374 remove_unselected_from_views (prev_amount, true);
2376 if (allow_moves_across_tracks) {
2378 // if regions were dragged across tracks, we've rippled any later
2379 // regions on the track the regions were dragged off, so we need
2380 // to add the original track to the undo record
2381 orig_tav->playlist()->clear_changes();
2382 vector<Command*> cmds;
2383 orig_tav->playlist()->rdiff (cmds);
2384 _editor->session()->add_commands (cmds);
2386 if (prev_tav && prev_tav != orig_tav) {
2387 prev_tav->playlist()->clear_changes();
2388 vector<Command*> cmds;
2389 prev_tav->playlist()->rdiff (cmds);
2390 _editor->session()->add_commands (cmds);
2393 // selection spanned multiple tracks - all will need adding to undo record
2395 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2396 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2398 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2399 (*pi)->clear_changes();
2400 vector<Command*> cmds;
2401 (*pi)->rdiff (cmds);
2402 _editor->session()->add_commands (cmds);
2406 // other modified playlists are added to undo by RegionMoveDrag::finished()
2407 RegionMoveDrag::finished (event, movement_occurred);
2408 _editor->commit_reversible_command();
2412 RegionRippleDrag::aborted (bool movement_occurred)
2414 RegionMoveDrag::aborted (movement_occurred);
2419 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2421 _view (dynamic_cast<MidiTimeAxisView*> (v))
2423 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2429 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2433 _editor->begin_reversible_command (_("create region"));
2434 _region = add_midi_region (_view, false);
2435 _view->playlist()->freeze ();
2439 framepos_t const f = adjusted_current_frame (event);
2440 if (f <= grab_frame()) {
2441 _region->set_initial_position (f);
2444 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2445 so that if this region is duplicated, its duplicate starts on
2446 a snap point rather than 1 frame after a snap point. Otherwise things get
2447 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2448 place snapped notes at the start of the region.
2450 if (f != grab_frame()) {
2451 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2452 _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2459 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2461 if (!movement_occurred) {
2462 add_midi_region (_view, true);
2464 _view->playlist()->thaw ();
2465 _editor->commit_reversible_command();
2470 RegionCreateDrag::aborted (bool)
2473 _view->playlist()->thaw ();
2479 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2484 , _was_selected (false)
2487 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2491 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2493 Gdk::Cursor* cursor;
2494 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2496 float x_fraction = cnote->mouse_x_fraction ();
2498 if (x_fraction > 0.0 && x_fraction < 0.25) {
2499 cursor = _editor->cursors()->left_side_trim;
2502 cursor = _editor->cursors()->right_side_trim;
2506 Drag::start_grab (event, cursor);
2508 region = &cnote->region_view();
2511 temp = region->snap_to_pixel (cnote->x0 (), true);
2512 _snap_delta = temp - cnote->x0 ();
2516 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2521 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2522 if (ms.size() > 1) {
2523 /* has to be relative, may make no sense otherwise */
2527 if (!(_was_selected = cnote->selected())) {
2529 /* tertiary-click means extend selection - we'll do that on button release,
2530 so don't add it here, because otherwise we make it hard to figure
2531 out the "extend-to" range.
2534 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2537 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2540 region->note_selected (cnote, true);
2542 _editor->get_selection().clear_points();
2543 region->unique_select (cnote);
2550 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2552 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2554 _editor->begin_reversible_command (_("resize notes"));
2556 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2557 MidiRegionSelection::iterator next;
2560 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2562 mrv->begin_resizing (at_front);
2568 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2569 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2571 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2575 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2577 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2578 if (_editor->snap_mode () != SnapOff) {
2582 if (_editor->snap_mode () == SnapOff) {
2584 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2585 if (apply_snap_delta) {
2591 if (apply_snap_delta) {
2595 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2601 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2603 if (!movement_occurred) {
2604 /* no motion - select note */
2605 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2606 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2607 _editor->current_mouse_mode() == Editing::MouseDraw) {
2609 bool changed = false;
2611 if (_was_selected) {
2612 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2614 region->note_deselected (cnote);
2617 _editor->get_selection().clear_points();
2618 region->unique_select (cnote);
2622 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2623 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2625 if (!extend && !add && region->selection_size() > 1) {
2626 _editor->get_selection().clear_points();
2627 region->unique_select (cnote);
2629 } else if (extend) {
2630 region->note_selected (cnote, true, true);
2633 /* it was added during button press */
2639 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2640 _editor->commit_reversible_selection_op();
2647 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2648 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2649 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2651 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2654 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2656 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2657 if (_editor->snap_mode () != SnapOff) {
2661 if (_editor->snap_mode () == SnapOff) {
2663 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2664 if (apply_snap_delta) {
2670 if (apply_snap_delta) {
2674 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2678 _editor->commit_reversible_command ();
2682 NoteResizeDrag::aborted (bool)
2684 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2685 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2686 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2688 mrv->abort_resizing ();
2693 AVDraggingView::AVDraggingView (RegionView* v)
2696 initial_position = v->region()->position ();
2699 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2702 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2705 TrackViewList empty;
2707 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2708 std::list<RegionView*> views = rs.by_layer();
2711 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2712 RegionView* rv = (*i);
2713 if (!rv->region()->video_locked()) {
2716 if (rv->region()->locked()) {
2719 _views.push_back (AVDraggingView (rv));
2724 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2726 Drag::start_grab (event);
2727 if (_editor->session() == 0) {
2731 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2737 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2741 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2742 _max_backwards_drag = (
2743 ARDOUR_UI::instance()->video_timeline->get_duration()
2744 + ARDOUR_UI::instance()->video_timeline->get_offset()
2745 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2748 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2749 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2750 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2753 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2756 Timecode::Time timecode;
2757 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2758 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);
2759 show_verbose_cursor_text (buf);
2763 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2765 if (_editor->session() == 0) {
2768 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2772 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2776 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2777 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2779 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2780 dt = - _max_backwards_drag;
2783 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2784 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2786 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2787 RegionView* rv = i->view;
2788 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2791 rv->region()->clear_changes ();
2792 rv->region()->suspend_property_changes();
2794 rv->region()->set_position(i->initial_position + dt);
2795 rv->region_changed(ARDOUR::Properties::position);
2798 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2799 Timecode::Time timecode;
2800 Timecode::Time timediff;
2802 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2803 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2804 snprintf (buf, sizeof (buf),
2805 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2806 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2807 , _("Video Start:"),
2808 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2810 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2812 show_verbose_cursor_text (buf);
2816 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2818 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2825 if (!movement_occurred || ! _editor->session()) {
2829 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2831 _editor->begin_reversible_command (_("Move Video"));
2833 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2834 ARDOUR_UI::instance()->video_timeline->save_undo();
2835 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2836 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2838 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2839 i->view->drag_end();
2840 i->view->region()->resume_property_changes ();
2842 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2845 _editor->session()->maybe_update_session_range(
2846 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2847 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2851 _editor->commit_reversible_command ();
2855 VideoTimeLineDrag::aborted (bool)
2857 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2860 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2861 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2863 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2864 i->view->region()->resume_property_changes ();
2865 i->view->region()->set_position(i->initial_position);
2869 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2870 : RegionDrag (e, i, p, v)
2871 , _operation (StartTrim)
2872 , _preserve_fade_anchor (preserve_fade_anchor)
2873 , _jump_position_when_done (false)
2875 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2879 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2882 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2883 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2885 if (tv && tv->is_track()) {
2886 speed = tv->track()->speed();
2889 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2890 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2891 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2893 framepos_t const pf = adjusted_current_frame (event);
2894 setup_snap_delta (MusicFrame(region_start, 0));
2896 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2897 /* Move the contents of the region around without changing the region bounds */
2898 _operation = ContentsTrim;
2899 Drag::start_grab (event, _editor->cursors()->trimmer);
2901 /* These will get overridden for a point trim.*/
2902 if (pf < (region_start + region_length/2)) {
2903 /* closer to front */
2904 _operation = StartTrim;
2905 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2906 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2908 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2912 _operation = EndTrim;
2913 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2914 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2916 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2920 /* jump trim disabled for now
2921 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2922 _jump_position_when_done = true;
2926 switch (_operation) {
2928 show_verbose_cursor_time (region_start);
2931 show_verbose_cursor_duration (region_start, region_end);
2934 show_verbose_cursor_time (pf);
2938 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2939 i->view->region()->suspend_property_changes ();
2944 TrimDrag::motion (GdkEvent* event, bool first_move)
2946 RegionView* rv = _primary;
2949 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2950 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2951 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2952 frameoffset_t frame_delta = 0;
2954 if (tv && tv->is_track()) {
2955 speed = tv->track()->speed();
2957 MusicFrame adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2958 framecnt_t dt = adj_frame.frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2964 switch (_operation) {
2966 trim_type = "Region start trim";
2969 trim_type = "Region end trim";
2972 trim_type = "Region content trim";
2979 _editor->begin_reversible_command (trim_type);
2981 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2982 RegionView* rv = i->view;
2983 rv->region()->playlist()->clear_owned_changes ();
2985 if (_operation == StartTrim) {
2986 rv->trim_front_starting ();
2989 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2992 arv->temporarily_hide_envelope ();
2996 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2997 insert_result = _editor->motion_frozen_playlists.insert (pl);
2999 if (insert_result.second) {
3003 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (rv);
3004 /* a MRV start trim may change the source length. ensure we cover all playlists here */
3005 if (mrv && _operation == StartTrim) {
3006 vector<boost::shared_ptr<Playlist> > all_playlists;
3007 _editor->session()->playlists->get (all_playlists);
3008 for (vector<boost::shared_ptr<Playlist> >::iterator x = all_playlists.begin(); x != all_playlists.end(); ++x) {
3010 if ((*x)->uses_source (rv->region()->source(0))) {
3011 insert_result = _editor->motion_frozen_playlists.insert (*x);
3012 if (insert_result.second) {
3013 (*x)->clear_owned_changes ();
3023 bool non_overlap_trim = false;
3025 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
3026 non_overlap_trim = true;
3029 /* contstrain trim to fade length */
3030 if (_preserve_fade_anchor) {
3031 switch (_operation) {
3033 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3034 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3036 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3037 if (ar->locked()) continue;
3038 framecnt_t len = ar->fade_in()->back()->when;
3039 if (len < dt) dt = min(dt, len);
3043 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3044 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3046 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3047 if (ar->locked()) continue;
3048 framecnt_t len = ar->fade_out()->back()->when;
3049 if (len < -dt) dt = max(dt, -len);
3057 switch (_operation) {
3059 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3060 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
3061 , adj_frame.division);
3063 if (changed && _preserve_fade_anchor) {
3064 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3066 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3067 framecnt_t len = ar->fade_in()->back()->when;
3068 framecnt_t diff = ar->first_frame() - i->initial_position;
3069 framepos_t new_length = len - diff;
3070 i->anchored_fade_length = min (ar->length(), new_length);
3071 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
3072 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
3079 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3080 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, adj_frame.division);
3081 if (changed && _preserve_fade_anchor) {
3082 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3084 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3085 framecnt_t len = ar->fade_out()->back()->when;
3086 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
3087 framepos_t new_length = len + diff;
3088 i->anchored_fade_length = min (ar->length(), new_length);
3089 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
3090 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
3098 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
3100 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3101 i->view->move_contents (frame_delta);
3107 switch (_operation) {
3109 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
3112 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
3115 // show_verbose_cursor_time (frame_delta);
3121 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
3123 if (movement_occurred) {
3124 motion (event, false);
3126 if (_operation == StartTrim) {
3127 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3129 /* This must happen before the region's StatefulDiffCommand is created, as it may
3130 `correct' (ahem) the region's _start from being negative to being zero. It
3131 needs to be zero in the undo record.
3133 i->view->trim_front_ending ();
3135 if (_preserve_fade_anchor) {
3136 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3138 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3139 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3140 ar->set_fade_in_length(i->anchored_fade_length);
3141 ar->set_fade_in_active(true);
3144 if (_jump_position_when_done) {
3145 i->view->region()->set_position (i->initial_position);
3148 } else if (_operation == EndTrim) {
3149 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3150 if (_preserve_fade_anchor) {
3151 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3153 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3154 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3155 ar->set_fade_out_length(i->anchored_fade_length);
3156 ar->set_fade_out_active(true);
3159 if (_jump_position_when_done) {
3160 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3165 if (!_editor->selection->selected (_primary)) {
3166 _primary->thaw_after_trim ();
3168 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3169 i->view->thaw_after_trim ();
3170 i->view->enable_display (true);
3174 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3175 /* Trimming one region may affect others on the playlist, so we need
3176 to get undo Commands from the whole playlist rather than just the
3177 region. Use motion_frozen_playlists (a set) to make sure we don't
3178 diff a given playlist more than once.
3181 vector<Command*> cmds;
3183 _editor->session()->add_commands (cmds);
3187 _editor->motion_frozen_playlists.clear ();
3188 _editor->commit_reversible_command();
3191 /* no mouse movement */
3192 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false).frame) {
3193 _editor->point_trim (event, adjusted_current_frame (event));
3197 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3198 i->view->region()->resume_property_changes ();
3203 TrimDrag::aborted (bool movement_occurred)
3205 /* Our motion method is changing model state, so use the Undo system
3206 to cancel. Perhaps not ideal, as this will leave an Undo point
3207 behind which may be slightly odd from the user's point of view.
3211 finished (&ev, true);
3213 if (movement_occurred) {
3214 _editor->session()->undo (1);
3217 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3218 i->view->region()->resume_property_changes ();
3223 TrimDrag::setup_pointer_frame_offset ()
3225 list<DraggingView>::iterator i = _views.begin ();
3226 while (i != _views.end() && i->view != _primary) {
3230 if (i == _views.end()) {
3234 switch (_operation) {
3236 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3239 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3246 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3249 , _old_snap_type (e->snap_type())
3250 , _old_snap_mode (e->snap_mode())
3253 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3254 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3256 _real_section = &_marker->meter();
3261 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3263 Drag::start_grab (event, cursor);
3264 show_verbose_cursor_time (adjusted_current_frame(event));
3268 MeterMarkerDrag::setup_pointer_frame_offset ()
3270 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3274 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3277 // create a dummy marker to catch events, then hide it.
3280 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3282 _marker = new MeterMarker (
3284 *_editor->meter_group,
3285 UIConfiguration::instance().color ("meter marker"),
3287 *new MeterSection (_marker->meter())
3290 /* use the new marker for the grab */
3291 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3294 TempoMap& map (_editor->session()->tempo_map());
3295 /* get current state */
3296 before_state = &map.get_state();
3299 _editor->begin_reversible_command (_("move meter mark"));
3301 _editor->begin_reversible_command (_("copy meter mark"));
3303 Timecode::BBT_Time bbt = _real_section->bbt();
3305 /* we can't add a meter where one currently exists */
3306 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3311 const double beat = map.beat_at_bbt (bbt);
3312 const framepos_t frame = map.frame_at_beat (beat);
3313 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3314 , beat, bbt, frame, _real_section->position_lock_style());
3315 if (!_real_section) {
3321 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3322 if (_real_section->position_lock_style() != AudioTime) {
3323 _editor->set_snap_to (SnapToBar);
3324 _editor->set_snap_mode (SnapNormal);
3328 framepos_t pf = adjusted_current_frame (event);
3330 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3331 /* never snap to music for audio locked */
3332 pf = adjusted_current_frame (event, false);
3335 _editor->session()->tempo_map().gui_set_meter_position (_real_section, pf);
3337 /* fake marker meeds to stay under the mouse, unlike the real one. */
3338 _marker->set_position (adjusted_current_frame (event, false));
3340 show_verbose_cursor_time (_real_section->frame());
3344 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3346 if (!movement_occurred) {
3347 if (was_double_click()) {
3348 _editor->edit_meter_marker (*_marker);
3353 /* reinstate old snap setting */
3354 _editor->set_snap_to (_old_snap_type);
3355 _editor->set_snap_mode (_old_snap_mode);
3357 TempoMap& map (_editor->session()->tempo_map());
3359 XMLNode &after = map.get_state();
3360 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3361 _editor->commit_reversible_command ();
3363 // delete the dummy marker we used for visual representation while moving.
3364 // a new visual marker will show up automatically.
3369 MeterMarkerDrag::aborted (bool moved)
3371 _marker->set_position (_marker->meter().frame ());
3373 /* reinstate old snap setting */
3374 _editor->set_snap_to (_old_snap_type);
3375 _editor->set_snap_mode (_old_snap_mode);
3377 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3378 // delete the dummy marker we used for visual representation while moving.
3379 // a new visual marker will show up automatically.
3384 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3387 , _grab_bpm (120.0, 4.0)
3391 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3393 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3394 _real_section = &_marker->tempo();
3395 _movable = !_real_section->initial();
3396 _grab_bpm = Tempo (_real_section->note_types_per_minute(), _real_section->note_type(), _real_section->end_note_types_per_minute());
3397 _grab_qn = _real_section->pulse() * 4.0;
3402 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3404 Drag::start_grab (event, cursor);
3405 if (!_real_section->active()) {
3406 show_verbose_cursor_text (_("inactive"));
3408 show_verbose_cursor_time (adjusted_current_frame (event));
3413 TempoMarkerDrag::setup_pointer_frame_offset ()
3415 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3419 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3421 if (!_real_section->active()) {
3424 TempoMap& map (_editor->session()->tempo_map());
3428 // mvc drag - create a dummy marker to catch events, hide it.
3431 snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute());
3433 TempoSection section (_marker->tempo());
3435 _marker = new TempoMarker (
3437 *_editor->tempo_group,
3438 UIConfiguration::instance().color ("tempo marker"),
3440 *new TempoSection (_marker->tempo())
3443 /* use the new marker for the grab */
3444 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3447 /* get current state */
3448 _before_state = &map.get_state();
3451 _editor->begin_reversible_command (_("move tempo mark"));
3454 const Tempo tempo (_marker->tempo());
3455 const framepos_t frame = adjusted_current_frame (event) + 1;
3457 _editor->begin_reversible_command (_("copy tempo mark"));
3459 if (_real_section->position_lock_style() == MusicTime) {
3460 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
3461 _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, MusicTime);
3463 _real_section = map.add_tempo (tempo, 0.0, frame, AudioTime);
3466 if (!_real_section) {
3473 if (ArdourKeyboard::indicates_constraint (event->button.state) && ArdourKeyboard::indicates_copy (event->button.state)) {
3474 double new_bpm = max (1.5, _grab_bpm.end_note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3476 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (_real_section->note_types_per_minute(), _real_section->note_type(), new_bpm));
3477 strs << "end:" << fixed << setprecision(3) << new_bpm;
3478 show_verbose_cursor_text (strs.str());
3480 } else if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3481 /* use vertical movement to alter tempo .. should be log */
3482 double new_bpm = max (1.5, _grab_bpm.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3484 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type(), _real_section->end_note_types_per_minute()));
3485 strs << "start:" << fixed << setprecision(3) << new_bpm;
3486 show_verbose_cursor_text (strs.str());
3488 } else if (_movable && !_real_section->locked_to_meter()) {
3491 if (_editor->snap_musical()) {
3492 /* we can't snap to a grid that we are about to move.
3493 * gui_move_tempo() will sort out snap using the supplied beat divisions.
3495 pf = adjusted_current_frame (event, false);
3497 pf = adjusted_current_frame (event);
3500 /* snap to beat is 1, snap to bar is -1 (sorry) */
3501 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3503 map.gui_set_tempo_position (_real_section, pf, sub_num);
3505 show_verbose_cursor_time (_real_section->frame());
3507 _marker->set_position (adjusted_current_frame (event, false));
3511 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3513 if (!_real_section->active()) {
3516 if (!movement_occurred) {
3517 if (was_double_click()) {
3518 _editor->edit_tempo_marker (*_marker);
3523 TempoMap& map (_editor->session()->tempo_map());
3525 XMLNode &after = map.get_state();
3526 _editor->session()->add_command (new MementoCommand<TempoMap>(map, _before_state, &after));
3527 _editor->commit_reversible_command ();
3529 // delete the dummy marker we used for visual representation while moving.
3530 // a new visual marker will show up automatically.
3535 TempoMarkerDrag::aborted (bool moved)
3537 _marker->set_position (_marker->tempo().frame());
3539 TempoMap& map (_editor->session()->tempo_map());
3540 map.set_state (*_before_state, Stateful::current_state_version);
3541 // delete the dummy (hidden) marker we used for events while moving.
3546 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3551 , _drag_valid (true)
3553 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3558 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3560 Drag::start_grab (event, cursor);
3561 TempoMap& map (_editor->session()->tempo_map());
3562 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3564 if (adjusted_current_frame (event, false) <= _tempo->frame()) {
3565 _drag_valid = false;
3569 _editor->tempo_curve_selected (_tempo, true);
3572 if (_tempo->clamped()) {
3573 TempoSection* prev = map.previous_tempo_section (_tempo);
3575 sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
3579 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3580 show_verbose_cursor_text (sstr.str());
3584 BBTRulerDrag::setup_pointer_frame_offset ()
3586 TempoMap& map (_editor->session()->tempo_map());
3587 /* get current state */
3588 _before_state = &map.get_state();
3590 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3591 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3594 if (divisions > 0) {
3595 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3597 /* while it makes some sense for the user to determine the division to 'grab',
3598 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3599 and the result over steep tempo curves. Use sixteenths.
3601 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3604 _grab_qn = map.quarter_note_at_beat (beat);
3606 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3611 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3618 _editor->begin_reversible_command (_("stretch tempo"));
3621 TempoMap& map (_editor->session()->tempo_map());
3624 if (_editor->snap_musical()) {
3625 pf = adjusted_current_frame (event, false);
3627 pf = adjusted_current_frame (event);
3630 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3631 /* adjust previous tempo to match pointer frame */
3632 _editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
3636 if (_tempo->clamped()) {
3637 TempoSection* prev = map.previous_tempo_section (_tempo);
3639 _editor->tempo_curve_selected (prev, true);
3640 sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
3643 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3644 show_verbose_cursor_text (sstr.str());
3648 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3650 if (!movement_occurred) {
3654 TempoMap& map (_editor->session()->tempo_map());
3656 _editor->tempo_curve_selected (_tempo, false);
3657 if (_tempo->clamped()) {
3658 TempoSection* prev_tempo = map.previous_tempo_section (_tempo);
3660 _editor->tempo_curve_selected (prev_tempo, false);
3664 if (!movement_occurred || !_drag_valid) {
3668 XMLNode &after = map.get_state();
3669 _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
3670 _editor->commit_reversible_command ();
3675 BBTRulerDrag::aborted (bool moved)
3678 _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
3682 TempoTwistDrag::TempoTwistDrag (Editor* e, ArdourCanvas::Item* i)
3688 , _drag_valid (true)
3691 DEBUG_TRACE (DEBUG::Drags, "New TempoTwistDrag\n");
3696 TempoTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3698 Drag::start_grab (event, cursor);
3699 TempoMap& map (_editor->session()->tempo_map());
3700 /* get current state */
3701 _before_state = &map.get_state();
3702 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3704 if (_tempo->locked_to_meter()) {
3705 _drag_valid = false;
3709 _next_tempo = map.next_tempo_section (_tempo);
3711 if (!map.next_tempo_section (_next_tempo)) {
3712 _drag_valid = false;
3713 finished (event, false);
3717 _editor->tempo_curve_selected (_tempo, true);
3718 _editor->tempo_curve_selected (_next_tempo, true);
3721 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
3722 sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
3723 sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
3724 show_verbose_cursor_text (sstr.str());
3726 _drag_valid = false;
3729 _grab_tempo = Tempo (_tempo->note_types_per_minute(), _tempo->note_type());
3733 TempoTwistDrag::setup_pointer_frame_offset ()
3735 TempoMap& map (_editor->session()->tempo_map());
3736 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3737 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3740 if (divisions > 0) {
3741 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3743 /* while it makes some sense for the user to determine the division to 'grab',
3744 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3745 and the result over steep tempo curves. Use sixteenths.
3747 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3750 _grab_qn = map.quarter_note_at_beat (beat);
3752 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3757 TempoTwistDrag::motion (GdkEvent* event, bool first_move)
3760 if (!_next_tempo || !_drag_valid) {
3764 TempoMap& map (_editor->session()->tempo_map());
3767 _editor->begin_reversible_command (_("twist tempo"));
3772 if (_editor->snap_musical()) {
3773 pf = adjusted_current_frame (event, false);
3775 pf = adjusted_current_frame (event);
3778 /* adjust this and the next tempi to match pointer frame */
3779 double new_bpm = max (1.5, _grab_tempo.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3780 _editor->session()->tempo_map().gui_twist_tempi (_tempo, new_bpm, map.frame_at_quarter_note (_grab_qn), pf);
3783 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
3784 sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
3785 sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
3786 show_verbose_cursor_text (sstr.str());
3790 TempoTwistDrag::finished (GdkEvent* event, bool movement_occurred)
3792 if (!movement_occurred || !_drag_valid) {
3796 _editor->tempo_curve_selected (_tempo, false);
3797 _editor->tempo_curve_selected (_next_tempo, false);
3799 TempoMap& map (_editor->session()->tempo_map());
3800 XMLNode &after = map.get_state();
3801 _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
3802 _editor->commit_reversible_command ();
3806 TempoTwistDrag::aborted (bool moved)
3809 _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
3813 TempoEndDrag::TempoEndDrag (Editor* e, ArdourCanvas::Item* i)
3818 , _drag_valid (true)
3820 DEBUG_TRACE (DEBUG::Drags, "New TempoEndDrag\n");
3821 TempoMarker* marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3822 _tempo = &marker->tempo();
3823 _grab_qn = _tempo->pulse() * 4.0;
3827 TempoEndDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3829 Drag::start_grab (event, cursor);
3830 TempoMap& tmap (_editor->session()->tempo_map());
3832 /* get current state */
3833 _before_state = &tmap.get_state();
3835 if (_tempo->locked_to_meter()) {
3836 _drag_valid = false;
3842 TempoSection* prev = 0;
3843 if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
3844 _editor->tempo_curve_selected (tmap.previous_tempo_section (_tempo), true);
3845 sstr << "end: " << fixed << setprecision(3) << tmap.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
3848 if (_tempo->clamped()) {
3849 _editor->tempo_curve_selected (_tempo, true);
3850 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3853 show_verbose_cursor_text (sstr.str());
3857 TempoEndDrag::setup_pointer_frame_offset ()
3859 TempoMap& map (_editor->session()->tempo_map());
3861 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3866 TempoEndDrag::motion (GdkEvent* event, bool first_move)
3872 TempoMap& map (_editor->session()->tempo_map());
3875 _editor->begin_reversible_command (_("stretch end tempo"));
3878 framepos_t const pf = adjusted_current_frame (event, false);
3879 map.gui_stretch_tempo_end (&map.tempo_section_at_frame (_tempo->frame() - 1), map.frame_at_quarter_note (_grab_qn), pf);
3882 sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
3884 if (_tempo->clamped()) {
3885 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3888 show_verbose_cursor_text (sstr.str());
3892 TempoEndDrag::finished (GdkEvent* event, bool movement_occurred)
3894 if (!movement_occurred || !_drag_valid) {
3898 TempoMap& tmap (_editor->session()->tempo_map());
3900 XMLNode &after = tmap.get_state();
3901 _editor->session()->add_command(new MementoCommand<TempoMap>(tmap, _before_state, &after));
3902 _editor->commit_reversible_command ();
3904 TempoSection* prev = 0;
3905 if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
3906 _editor->tempo_curve_selected (prev, false);
3909 if (_tempo->clamped()) {
3910 _editor->tempo_curve_selected (_tempo, false);
3916 TempoEndDrag::aborted (bool moved)
3919 _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
3923 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3924 : Drag (e, &c.track_canvas_item(), false)
3929 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3932 /** Do all the things we do when dragging the playhead to make it look as though
3933 * we have located, without actually doing the locate (because that would cause
3934 * the diskstream buffers to be refilled, which is too slow).
3937 CursorDrag::fake_locate (framepos_t t)
3939 if (_editor->session () == 0) {
3943 _editor->playhead_cursor->set_position (t);
3945 Session* s = _editor->session ();
3946 if (s->timecode_transmission_suspended ()) {
3947 framepos_t const f = _editor->playhead_cursor->current_frame ();
3948 /* This is asynchronous so it will be sent "now"
3950 s->send_mmc_locate (f);
3951 /* These are synchronous and will be sent during the next
3954 s->queue_full_time_code ();
3955 s->queue_song_position_pointer ();
3958 show_verbose_cursor_time (t);
3959 _editor->UpdateAllTransportClocks (t);
3963 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3965 Drag::start_grab (event, c);
3966 setup_snap_delta (MusicFrame (_editor->playhead_cursor->current_frame(), 0));
3968 _grab_zoom = _editor->samples_per_pixel;
3970 MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3972 _editor->snap_to_with_modifier (where, event);
3973 _editor->_dragging_playhead = true;
3974 _editor->_control_scroll_target = where.frame;
3976 Session* s = _editor->session ();
3978 /* grab the track canvas item as well */
3980 _cursor.track_canvas_item().grab();
3983 if (_was_rolling && _stop) {
3987 if (s->is_auditioning()) {
3988 s->cancel_audition ();
3992 if (AudioEngine::instance()->running()) {
3994 /* do this only if we're the engine is connected
3995 * because otherwise this request will never be
3996 * serviced and we'll busy wait forever. likewise,
3997 * notice if we are disconnected while waiting for the
3998 * request to be serviced.
4001 s->request_suspend_timecode_transmission ();
4002 while (AudioEngine::instance()->running() && !s->timecode_transmission_suspended ()) {
4003 /* twiddle our thumbs */
4008 fake_locate (where.frame - snap_delta (event->button.state));
4014 CursorDrag::motion (GdkEvent* event, bool)
4016 MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4018 _editor->snap_to_with_modifier (where, event);
4020 if (where.frame != last_pointer_frame()) {
4021 fake_locate (where.frame - snap_delta (event->button.state));
4024 //maybe do zooming, too, if the option is enabled
4025 if (UIConfiguration::instance ().get_use_time_rulers_to_zoom_with_vertical_drag () ) {
4027 //To avoid accidental zooming, the mouse must move exactly vertical, not diagonal, to trigger a zoom step
4028 //we use screen coordinates for this, not canvas-based grab_x
4029 double mx = event->button.x;
4030 double dx = fabs(mx - _last_mx);
4031 double my = event->button.y;
4032 double dy = fabs(my - _last_my);
4035 //do zooming in windowed "steps" so it feels more reversible ?
4036 const int stepsize = 2; //stepsize ==1 means "trigger on every pixel of movement"
4037 int y_delta = grab_y() - current_pointer_y();
4038 y_delta = y_delta / stepsize;
4040 //if all requirements are met, do the actual zoom
4041 const double scale = 1.2;
4042 if ( (dy>dx) && (_last_dx ==0) && (y_delta != _last_y_delta) ) {
4043 if ( _last_y_delta > y_delta ) {
4044 _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
4046 _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
4048 _last_y_delta = y_delta;
4059 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
4061 _editor->_dragging_playhead = false;
4063 _cursor.track_canvas_item().ungrab();
4065 if (!movement_occurred && _stop) {
4069 motion (event, false);
4071 Session* s = _editor->session ();
4073 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
4074 _editor->_pending_locate_request = true;
4075 s->request_resume_timecode_transmission ();
4080 CursorDrag::aborted (bool)
4082 _cursor.track_canvas_item().ungrab();
4084 if (_editor->_dragging_playhead) {
4085 _editor->session()->request_resume_timecode_transmission ();
4086 _editor->_dragging_playhead = false;
4089 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false).frame);
4092 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
4093 : RegionDrag (e, i, p, v)
4095 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
4099 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4101 Drag::start_grab (event, cursor);
4103 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4104 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
4105 setup_snap_delta (MusicFrame (r->position(), 0));
4107 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
4111 FadeInDrag::setup_pointer_frame_offset ()
4113 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4114 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
4115 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
4119 FadeInDrag::motion (GdkEvent* event, bool)
4121 framecnt_t fade_length;
4123 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4124 _editor->snap_to_with_modifier (pos, event);
4126 pos.frame -= snap_delta (event->button.state);
4128 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4130 if (pos.frame < (region->position() + 64)) {
4131 fade_length = 64; // this should be a minimum defined somewhere
4132 } else if (pos.frame > region->position() + region->length() - region->fade_out()->back()->when) {
4133 fade_length = region->length() - region->fade_out()->back()->when - 1;
4135 fade_length = pos.frame - region->position();
4138 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4140 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4146 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
4149 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
4153 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
4155 if (!movement_occurred) {
4159 framecnt_t fade_length;
4160 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4162 _editor->snap_to_with_modifier (pos, event);
4163 pos.frame -= snap_delta (event->button.state);
4165 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4167 if (pos.frame < (region->position() + 64)) {
4168 fade_length = 64; // this should be a minimum defined somewhere
4169 } else if (pos.frame >= region->position() + region->length() - region->fade_out()->back()->when) {
4170 fade_length = region->length() - region->fade_out()->back()->when - 1;
4172 fade_length = pos.frame - region->position();
4175 bool in_command = false;
4177 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4179 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4185 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
4186 XMLNode &before = alist->get_state();
4188 tmp->audio_region()->set_fade_in_length (fade_length);
4189 tmp->audio_region()->set_fade_in_active (true);
4192 _editor->begin_reversible_command (_("change fade in length"));
4195 XMLNode &after = alist->get_state();
4196 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4200 _editor->commit_reversible_command ();
4205 FadeInDrag::aborted (bool)
4207 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4208 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4214 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
4218 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
4219 : RegionDrag (e, i, p, v)
4221 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
4225 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4227 Drag::start_grab (event, cursor);
4229 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4230 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
4231 setup_snap_delta (MusicFrame (r->last_frame(), 0));
4233 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
4237 FadeOutDrag::setup_pointer_frame_offset ()
4239 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4240 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
4241 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
4245 FadeOutDrag::motion (GdkEvent* event, bool)
4247 framecnt_t fade_length;
4248 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4250 _editor->snap_to_with_modifier (pos, event);
4251 pos.frame -= snap_delta (event->button.state);
4253 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4255 if (pos.frame > (region->last_frame() - 64)) {
4256 fade_length = 64; // this should really be a minimum fade defined somewhere
4257 } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
4258 fade_length = region->length() - region->fade_in()->back()->when - 1;
4260 fade_length = region->last_frame() - pos.frame;
4263 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4265 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4271 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
4274 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
4278 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
4280 if (!movement_occurred) {
4284 framecnt_t fade_length;
4285 MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4287 _editor->snap_to_with_modifier (pos, event);
4288 pos.frame -= snap_delta (event->button.state);
4290 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4292 if (pos.frame > (region->last_frame() - 64)) {
4293 fade_length = 64; // this should really be a minimum fade defined somewhere
4294 } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
4295 fade_length = region->length() - region->fade_in()->back()->when - 1;
4297 fade_length = region->last_frame() - pos.frame;
4300 bool in_command = false;
4302 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4304 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4310 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
4311 XMLNode &before = alist->get_state();
4313 tmp->audio_region()->set_fade_out_length (fade_length);
4314 tmp->audio_region()->set_fade_out_active (true);
4317 _editor->begin_reversible_command (_("change fade out length"));
4320 XMLNode &after = alist->get_state();
4321 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4325 _editor->commit_reversible_command ();
4330 FadeOutDrag::aborted (bool)
4332 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4333 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4339 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
4343 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
4345 , _selection_changed (false)
4347 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
4348 Gtk::Window* toplevel = _editor->current_toplevel();
4349 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
4353 _points.push_back (ArdourCanvas::Duple (0, 0));
4355 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
4358 MarkerDrag::~MarkerDrag ()
4360 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
4365 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
4367 location = new Location (*l);
4368 markers.push_back (m);
4373 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4375 Drag::start_grab (event, cursor);
4379 Location *location = _editor->find_location_from_marker (_marker, is_start);
4380 _editor->_dragging_edit_point = true;
4382 update_item (location);
4384 // _drag_line->show();
4385 // _line->raise_to_top();
4388 show_verbose_cursor_time (location->start());
4390 show_verbose_cursor_time (location->end());
4392 setup_snap_delta (MusicFrame (is_start ? location->start() : location->end(), 0));
4394 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4397 case Selection::Toggle:
4398 /* we toggle on the button release */
4400 case Selection::Set:
4401 if (!_editor->selection->selected (_marker)) {
4402 _editor->selection->set (_marker);
4403 _selection_changed = true;
4406 case Selection::Extend:
4408 Locations::LocationList ll;
4409 list<ArdourMarker*> to_add;
4411 _editor->selection->markers.range (s, e);
4412 s = min (_marker->position(), s);
4413 e = max (_marker->position(), e);
4416 if (e < max_framepos) {
4419 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
4420 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
4421 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
4424 to_add.push_back (lm->start);
4427 to_add.push_back (lm->end);
4431 if (!to_add.empty()) {
4432 _editor->selection->add (to_add);
4433 _selection_changed = true;
4437 case Selection::Add:
4438 _editor->selection->add (_marker);
4439 _selection_changed = true;
4444 /* Set up copies for us to manipulate during the drag
4447 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4449 Location* l = _editor->find_location_from_marker (*i, is_start);
4456 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4458 /* range: check that the other end of the range isn't
4461 CopiedLocationInfo::iterator x;
4462 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4463 if (*(*x).location == *l) {
4467 if (x == _copied_locations.end()) {
4468 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4470 (*x).markers.push_back (*i);
4471 (*x).move_both = true;
4479 MarkerDrag::setup_pointer_frame_offset ()
4482 Location *location = _editor->find_location_from_marker (_marker, is_start);
4483 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4487 MarkerDrag::motion (GdkEvent* event, bool)
4489 framecnt_t f_delta = 0;
4491 bool move_both = false;
4492 Location *real_location;
4493 Location *copy_location = 0;
4494 framecnt_t const sd = snap_delta (event->button.state);
4496 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true).frame - sd;
4497 framepos_t next = newframe;
4499 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4503 CopiedLocationInfo::iterator x;
4505 /* find the marker we're dragging, and compute the delta */
4507 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4509 copy_location = (*x).location;
4511 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4513 /* this marker is represented by this
4514 * CopiedLocationMarkerInfo
4517 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4522 if (real_location->is_mark()) {
4523 f_delta = newframe - copy_location->start();
4527 switch (_marker->type()) {
4528 case ArdourMarker::SessionStart:
4529 case ArdourMarker::RangeStart:
4530 case ArdourMarker::LoopStart:
4531 case ArdourMarker::PunchIn:
4532 f_delta = newframe - copy_location->start();
4535 case ArdourMarker::SessionEnd:
4536 case ArdourMarker::RangeEnd:
4537 case ArdourMarker::LoopEnd:
4538 case ArdourMarker::PunchOut:
4539 f_delta = newframe - copy_location->end();
4542 /* what kind of marker is this ? */
4551 if (x == _copied_locations.end()) {
4552 /* hmm, impossible - we didn't find the dragged marker */
4556 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4558 /* now move them all */
4560 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4562 copy_location = x->location;
4564 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4568 if (real_location->locked()) {
4572 if (copy_location->is_mark()) {
4575 copy_location->set_start (copy_location->start() + f_delta, false, true, divisions);
4579 framepos_t new_start = copy_location->start() + f_delta;
4580 framepos_t new_end = copy_location->end() + f_delta;
4582 if (is_start) { // start-of-range marker
4584 if (move_both || (*x).move_both) {
4585 copy_location->set_start (new_start, false, true, divisions);
4586 copy_location->set_end (new_end, false, true, divisions);
4587 } else if (new_start < copy_location->end()) {
4588 copy_location->set_start (new_start, false, true, divisions);
4589 } else if (newframe > 0) {
4590 //_editor->snap_to (next, RoundUpAlways, true);
4591 copy_location->set_end (next, false, true, divisions);
4592 copy_location->set_start (newframe, false, true, divisions);
4595 } else { // end marker
4597 if (move_both || (*x).move_both) {
4598 copy_location->set_end (new_end, divisions);
4599 copy_location->set_start (new_start, false, true, divisions);
4600 } else if (new_end > copy_location->start()) {
4601 copy_location->set_end (new_end, false, true, divisions);
4602 } else if (newframe > 0) {
4603 //_editor->snap_to (next, RoundDownAlways, true);
4604 copy_location->set_start (next, false, true, divisions);
4605 copy_location->set_end (newframe, false, true, divisions);
4610 update_item (copy_location);
4612 /* now lookup the actual GUI items used to display this
4613 * location and move them to wherever the copy of the location
4614 * is now. This means that the logic in ARDOUR::Location is
4615 * still enforced, even though we are not (yet) modifying
4616 * the real Location itself.
4619 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4622 lm->set_position (copy_location->start(), copy_location->end());
4627 assert (!_copied_locations.empty());
4629 show_verbose_cursor_time (newframe);
4633 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4635 if (!movement_occurred) {
4637 if (was_double_click()) {
4638 _editor->rename_marker (_marker);
4642 /* just a click, do nothing but finish
4643 off the selection process
4646 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4648 case Selection::Set:
4649 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4650 _editor->selection->set (_marker);
4651 _selection_changed = true;
4655 case Selection::Toggle:
4656 /* we toggle on the button release, click only */
4657 _editor->selection->toggle (_marker);
4658 _selection_changed = true;
4662 case Selection::Extend:
4663 case Selection::Add:
4667 if (_selection_changed) {
4668 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4669 _editor->commit_reversible_selection_op();
4675 _editor->_dragging_edit_point = false;
4677 XMLNode &before = _editor->session()->locations()->get_state();
4678 bool in_command = false;
4680 MarkerSelection::iterator i;
4681 CopiedLocationInfo::iterator x;
4682 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4685 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4686 x != _copied_locations.end() && i != _editor->selection->markers.end();
4689 Location * location = _editor->find_location_from_marker (*i, is_start);
4693 if (location->locked()) {
4697 _editor->begin_reversible_command ( _("move marker") );
4700 if (location->is_mark()) {
4701 location->set_start (((*x).location)->start(), false, true, divisions);
4703 location->set (((*x).location)->start(), ((*x).location)->end(), true, divisions);
4706 if (location->is_session_range()) {
4707 _editor->session()->set_end_is_free (false);
4713 XMLNode &after = _editor->session()->locations()->get_state();
4714 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4715 _editor->commit_reversible_command ();
4720 MarkerDrag::aborted (bool movement_occurred)
4722 if (!movement_occurred) {
4726 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4728 /* move all markers to their original location */
4731 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4734 Location * location = _editor->find_location_from_marker (*m, is_start);
4737 (*m)->set_position (is_start ? location->start() : location->end());
4744 MarkerDrag::update_item (Location*)
4749 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4751 , _fixed_grab_x (0.0)
4752 , _fixed_grab_y (0.0)
4753 , _cumulative_x_drag (0.0)
4754 , _cumulative_y_drag (0.0)
4758 if (_zero_gain_fraction < 0.0) {
4759 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4762 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4764 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4770 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4772 Drag::start_grab (event, _editor->cursors()->fader);
4774 // start the grab at the center of the control point so
4775 // the point doesn't 'jump' to the mouse after the first drag
4776 _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4777 _fixed_grab_y = _point->get_y();
4779 setup_snap_delta (MusicFrame (_editor->pixel_to_sample (_fixed_grab_x), 0));
4781 float const fraction = 1 - (_point->get_y() / _point->line().height());
4782 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4784 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4786 if (!_point->can_slide ()) {
4787 _x_constrained = true;
4792 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4794 double dx = _drags->current_pointer_x() - last_pointer_x();
4795 double dy = current_pointer_y() - last_pointer_y();
4796 bool need_snap = true;
4798 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4804 /* coordinate in pixels relative to the start of the region (for region-based automation)
4805 or track (for track-based automation) */
4806 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4807 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4809 // calculate zero crossing point. back off by .01 to stay on the
4810 // positive side of zero
4811 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4813 if (_x_constrained) {
4816 if (_y_constrained) {
4820 _cumulative_x_drag = cx - _fixed_grab_x;
4821 _cumulative_y_drag = cy - _fixed_grab_y;
4825 cy = min ((double) _point->line().height(), cy);
4827 // make sure we hit zero when passing through
4828 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4832 MusicFrame cx_mf (_editor->pixel_to_sample (cx) + snap_delta (event->button.state), 0);
4834 if (!_x_constrained && need_snap) {
4835 _editor->snap_to_with_modifier (cx_mf, event);
4838 cx_mf.frame -= snap_delta (event->button.state);
4839 cx_mf.frame = min (cx_mf.frame, _point->line().maximum_time() + _point->line().offset());
4841 float const fraction = 1.0 - (cy / _point->line().height());
4844 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4845 _editor->begin_reversible_command (_("automation event move"));
4846 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4848 pair<float, float> result;
4849 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_mf.frame), fraction, false, _pushing, _final_index);
4850 show_verbose_cursor_text (_point->line().get_verbose_cursor_relative_string (result.first, result.second));
4854 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4856 if (!movement_occurred) {
4859 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4860 _editor->reset_point_selection ();
4864 _point->line().end_drag (_pushing, _final_index);
4865 _editor->commit_reversible_command ();
4870 ControlPointDrag::aborted (bool)
4872 _point->line().reset ();
4876 ControlPointDrag::active (Editing::MouseMode m)
4878 if (m == Editing::MouseDraw) {
4879 /* always active in mouse draw */
4883 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4884 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4887 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4890 , _fixed_grab_x (0.0)
4891 , _fixed_grab_y (0.0)
4892 , _cumulative_y_drag (0)
4896 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4900 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4902 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4905 _item = &_line->grab_item ();
4907 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4908 origin, and ditto for y.
4911 double mx = event->button.x;
4912 double my = event->button.y;
4914 _line->grab_item().canvas_to_item (mx, my);
4916 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4918 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4919 /* no adjacent points */
4923 Drag::start_grab (event, _editor->cursors()->fader);
4925 /* store grab start in item frame */
4926 double const bx = _line->nth (_before)->get_x();
4927 double const ax = _line->nth (_after)->get_x();
4928 double const click_ratio = (ax - mx) / (ax - bx);
4930 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4935 double fraction = 1.0 - (cy / _line->height());
4937 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4941 LineDrag::motion (GdkEvent* event, bool first_move)
4943 double dy = current_pointer_y() - last_pointer_y();
4945 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4949 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4951 _cumulative_y_drag = cy - _fixed_grab_y;
4954 cy = min ((double) _line->height(), cy);
4956 double const fraction = 1.0 - (cy / _line->height());
4960 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4962 _editor->begin_reversible_command (_("automation range move"));
4963 _line->start_drag_line (_before, _after, initial_fraction);
4966 /* we are ignoring x position for this drag, so we can just pass in anything */
4967 pair<float, float> result;
4969 result = _line->drag_motion (0, fraction, true, false, ignored);
4970 show_verbose_cursor_text (_line->get_verbose_cursor_relative_string (result.first, result.second));
4974 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4976 if (movement_occurred) {
4977 motion (event, false);
4978 _line->end_drag (false, 0);
4979 _editor->commit_reversible_command ();
4981 /* add a new control point on the line */
4983 AutomationTimeAxisView* atv;
4985 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4986 framepos_t where = grab_frame ();
4989 double cy = _fixed_grab_y;
4991 _line->grab_item().item_to_canvas (cx, cy);
4993 atv->add_automation_event (event, where, cy, false);
4994 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4995 AudioRegionView* arv;
4997 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4998 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
5005 LineDrag::aborted (bool)
5010 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
5014 _region_view_grab_x (0.0),
5015 _cumulative_x_drag (0),
5019 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
5023 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
5025 Drag::start_grab (event);
5027 _line = reinterpret_cast<Line*> (_item);
5030 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
5032 double cx = event->button.x;
5033 double cy = event->button.y;
5035 _item->parent()->canvas_to_item (cx, cy);
5037 /* store grab start in parent frame */
5038 _region_view_grab_x = cx;
5040 _before = *(float*) _item->get_data ("position");
5042 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
5044 _max_x = _editor->sample_to_pixel(_arv->get_duration());
5048 FeatureLineDrag::motion (GdkEvent*, bool)
5050 double dx = _drags->current_pointer_x() - last_pointer_x();
5052 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
5054 _cumulative_x_drag += dx;
5056 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
5065 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
5067 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
5069 float *pos = new float;
5072 _line->set_data ("position", pos);
5078 FeatureLineDrag::finished (GdkEvent*, bool)
5080 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
5081 _arv->update_transient(_before, _before);
5085 FeatureLineDrag::aborted (bool)
5090 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5092 , _vertical_only (false)
5094 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
5098 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5100 Drag::start_grab (event);
5101 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
5105 RubberbandSelectDrag::motion (GdkEvent* event, bool)
5111 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
5112 MusicFrame grab (grab_frame (), 0);
5114 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
5115 _editor->snap_to_with_modifier (grab, event);
5117 grab.frame = raw_grab_frame ();
5120 /* base start and end on initial click position */
5122 if (pf < grab.frame) {
5130 if (current_pointer_y() < grab_y()) {
5131 y1 = current_pointer_y();
5134 y2 = current_pointer_y();
5138 if (start != end || y1 != y2) {
5140 double x1 = _editor->sample_to_pixel (start);
5141 double x2 = _editor->sample_to_pixel (end);
5142 const double min_dimension = 2.0;
5144 if (_vertical_only) {
5145 /* fixed 10 pixel width */
5149 x2 = min (x1 - min_dimension, x2);
5151 x2 = max (x1 + min_dimension, x2);
5156 y2 = min (y1 - min_dimension, y2);
5158 y2 = max (y1 + min_dimension, y2);
5161 /* translate rect into item space and set */
5163 ArdourCanvas::Rect r (x1, y1, x2, y2);
5165 /* this drag is a _trackview_only == true drag, so the y1 and
5166 * y2 (computed using current_pointer_y() and grab_y()) will be
5167 * relative to the top of the trackview group). The
5168 * rubberband rect has the same parent/scroll offset as the
5169 * the trackview group, so we can use the "r" rect directly
5170 * to set the shape of the rubberband.
5173 _editor->rubberband_rect->set (r);
5174 _editor->rubberband_rect->show();
5175 _editor->rubberband_rect->raise_to_top();
5177 show_verbose_cursor_time (pf);
5179 do_select_things (event, true);
5184 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
5188 framepos_t grab = grab_frame ();
5189 framepos_t lpf = last_pointer_frame ();
5191 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
5192 grab = raw_grab_frame ();
5193 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
5207 if (current_pointer_y() < grab_y()) {
5208 y1 = current_pointer_y();
5211 y2 = current_pointer_y();
5215 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
5219 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
5221 if (movement_occurred) {
5223 motion (event, false);
5224 do_select_things (event, false);
5230 bool do_deselect = true;
5231 MidiTimeAxisView* mtv;
5233 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
5235 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
5236 /* nothing selected */
5237 add_midi_region (mtv, true);
5238 do_deselect = false;
5242 /* do not deselect if Primary or Tertiary (toggle-select or
5243 * extend-select are pressed.
5246 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
5247 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
5254 _editor->rubberband_rect->hide();
5258 RubberbandSelectDrag::aborted (bool)
5260 _editor->rubberband_rect->hide ();
5263 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
5264 : RegionDrag (e, i, p, v)
5266 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
5270 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5272 Drag::start_grab (event, cursor);
5274 _editor->get_selection().add (_primary);
5276 MusicFrame where (_primary->region()->position(), 0);
5277 setup_snap_delta (where);
5279 show_verbose_cursor_duration (where.frame, adjusted_current_frame (event), 0);
5283 TimeFXDrag::motion (GdkEvent* event, bool)
5285 RegionView* rv = _primary;
5286 StreamView* cv = rv->get_time_axis_view().view ();
5287 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
5288 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
5289 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
5290 MusicFrame pf (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
5292 _editor->snap_to_with_modifier (pf, event);
5293 pf.frame -= snap_delta (event->button.state);
5295 if (pf.frame > rv->region()->position()) {
5296 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf.frame, layers, layer);
5299 show_verbose_cursor_duration (_primary->region()->position(), pf.frame, 0);
5303 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
5305 /* this may have been a single click, no drag. We still want the dialog
5306 to show up in that case, so that the user can manually edit the
5307 parameters for the timestretch.
5310 float fraction = 1.0;
5312 if (movement_occurred) {
5314 motion (event, false);
5316 _primary->get_time_axis_view().hide_timestretch ();
5318 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
5320 if (adjusted_frame_pos < _primary->region()->position()) {
5321 /* backwards drag of the left edge - not usable */
5325 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
5327 fraction = (double) newlen / (double) _primary->region()->length();
5329 #ifndef USE_RUBBERBAND
5330 // Soundtouch uses fraction / 100 instead of normal (/ 1)
5331 if (_primary->region()->data_type() == DataType::AUDIO) {
5332 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
5337 if (!_editor->get_selection().regions.empty()) {
5338 /* primary will already be included in the selection, and edit
5339 group shared editing will propagate selection across
5340 equivalent regions, so just use the current region
5344 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
5345 error << _("An error occurred while executing time stretch operation") << endmsg;
5351 TimeFXDrag::aborted (bool)
5353 _primary->get_time_axis_view().hide_timestretch ();
5356 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
5359 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
5363 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5365 Drag::start_grab (event);
5369 ScrubDrag::motion (GdkEvent* /*event*/, bool)
5371 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
5375 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
5377 if (movement_occurred && _editor->session()) {
5378 /* make sure we stop */
5379 _editor->session()->request_transport_speed (0.0);
5384 ScrubDrag::aborted (bool)
5389 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5393 , _track_selection_at_start (e)
5394 , _time_selection_at_start (!_editor->get_selection().time.empty())
5396 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
5398 if (_time_selection_at_start) {
5399 start_at_start = _editor->get_selection().time.start();
5400 end_at_start = _editor->get_selection().time.end_frame();
5405 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
5407 if (_editor->session() == 0) {
5411 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5413 switch (_operation) {
5414 case CreateSelection:
5415 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5420 cursor = _editor->cursors()->selector;
5421 Drag::start_grab (event, cursor);
5424 case SelectionStartTrim:
5425 if (_editor->clicked_axisview) {
5426 _editor->clicked_axisview->order_selection_trims (_item, true);
5428 Drag::start_grab (event, _editor->cursors()->left_side_trim);
5431 case SelectionEndTrim:
5432 if (_editor->clicked_axisview) {
5433 _editor->clicked_axisview->order_selection_trims (_item, false);
5435 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5439 Drag::start_grab (event, cursor);
5442 case SelectionExtend:
5443 Drag::start_grab (event, cursor);
5447 if (_operation == SelectionMove) {
5448 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5450 show_verbose_cursor_time (adjusted_current_frame (event));
5455 SelectionDrag::setup_pointer_frame_offset ()
5457 switch (_operation) {
5458 case CreateSelection:
5459 _pointer_frame_offset = 0;
5462 case SelectionStartTrim:
5464 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5467 case SelectionEndTrim:
5468 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5471 case SelectionExtend:
5477 SelectionDrag::motion (GdkEvent* event, bool first_move)
5479 framepos_t start = 0;
5481 framecnt_t length = 0;
5482 framecnt_t distance = 0;
5483 MusicFrame start_mf (0, 0);
5484 framepos_t const pending_position = adjusted_current_frame (event);
5486 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5491 _track_selection_at_start = _editor->selection->tracks;
5494 switch (_operation) {
5495 case CreateSelection:
5497 MusicFrame grab (grab_frame (), 0);
5499 grab.frame = adjusted_current_frame (event, false);
5500 if (grab.frame < pending_position) {
5501 _editor->snap_to (grab, RoundDownMaybe);
5503 _editor->snap_to (grab, RoundUpMaybe);
5507 if (pending_position < grab.frame) {
5508 start = pending_position;
5511 end = pending_position;
5515 /* first drag: Either add to the selection
5516 or create a new selection
5523 /* adding to the selection */
5524 _editor->set_selected_track_as_side_effect (Selection::Add);
5525 _editor->clicked_selection = _editor->selection->add (start, end);
5532 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5533 _editor->set_selected_track_as_side_effect (Selection::Set);
5536 _editor->clicked_selection = _editor->selection->set (start, end);
5540 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5541 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5542 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5544 _editor->selection->add (atest);
5548 /* select all tracks within the rectangle that we've marked out so far */
5549 TrackViewList new_selection;
5550 TrackViewList& all_tracks (_editor->track_views);
5552 ArdourCanvas::Coord const top = grab_y();
5553 ArdourCanvas::Coord const bottom = current_pointer_y();
5555 if (top >= 0 && bottom >= 0) {
5557 //first, find the tracks that are covered in the y range selection
5558 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5559 if ((*i)->covered_by_y_range (top, bottom)) {
5560 new_selection.push_back (*i);
5564 //now compare our list with the current selection, and add as necessary
5565 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5566 TrackViewList tracks_to_add;
5567 TrackViewList tracks_to_remove;
5568 vector<RouteGroup*> selected_route_groups;
5571 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i) {
5572 if (!new_selection.contains (*i) && !_track_selection_at_start.contains (*i)) {
5573 tracks_to_remove.push_back (*i);
5575 RouteGroup* rg = (*i)->route_group();
5576 if (rg && rg->is_active() && rg->is_select()) {
5577 selected_route_groups.push_back (rg);
5583 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5584 if (!_editor->selection->tracks.contains (*i)) {
5585 tracks_to_add.push_back (*i);
5586 RouteGroup* rg = (*i)->route_group();
5588 if (rg && rg->is_active() && rg->is_select()) {
5589 selected_route_groups.push_back (rg);
5594 _editor->selection->add (tracks_to_add);
5596 if (!tracks_to_remove.empty()) {
5598 /* check all these to-be-removed tracks against the
5599 * possibility that they are selected by being
5600 * in the same group as an approved track.
5603 for (TrackViewList::iterator i = tracks_to_remove.begin(); i != tracks_to_remove.end(); ) {
5604 RouteGroup* rg = (*i)->route_group();
5606 if (rg && find (selected_route_groups.begin(), selected_route_groups.end(), rg) != selected_route_groups.end()) {
5607 i = tracks_to_remove.erase (i);
5613 /* remove whatever is left */
5615 _editor->selection->remove (tracks_to_remove);
5621 case SelectionStartTrim:
5623 end = _editor->selection->time[_editor->clicked_selection].end;
5625 if (pending_position > end) {
5628 start = pending_position;
5632 case SelectionEndTrim:
5634 start = _editor->selection->time[_editor->clicked_selection].start;
5636 if (pending_position < start) {
5639 end = pending_position;
5646 start = _editor->selection->time[_editor->clicked_selection].start;
5647 end = _editor->selection->time[_editor->clicked_selection].end;
5649 length = end - start;
5650 distance = pending_position - start;
5651 start = pending_position;
5653 start_mf.frame = start;
5654 _editor->snap_to (start_mf);
5656 end = start_mf.frame + length;
5660 case SelectionExtend:
5665 switch (_operation) {
5667 if (_time_selection_at_start) {
5668 _editor->selection->move_time (distance);
5672 _editor->selection->replace (_editor->clicked_selection, start, end);
5676 if (_operation == SelectionMove) {
5677 show_verbose_cursor_time(start);
5679 show_verbose_cursor_time(pending_position);
5684 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5686 Session* s = _editor->session();
5688 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5689 if (movement_occurred) {
5690 motion (event, false);
5691 /* XXX this is not object-oriented programming at all. ick */
5692 if (_editor->selection->time.consolidate()) {
5693 _editor->selection->TimeChanged ();
5696 /* XXX what if its a music time selection? */
5698 if (s->get_play_range() && s->transport_rolling()) {
5699 s->request_play_range (&_editor->selection->time, true);
5700 } else if (!s->config.get_external_sync()) {
5701 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5702 s->request_locate (_editor->get_selection().time.start());
5706 if (_editor->get_selection().time.length() != 0) {
5707 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5709 s->clear_range_selection ();
5714 /* just a click, no pointer movement.
5717 if (was_double_click()) {
5718 if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
5719 _editor->temporal_zoom_selection (Both);
5724 if (_operation == SelectionExtend) {
5725 if (_time_selection_at_start) {
5726 framepos_t pos = adjusted_current_frame (event, false);
5727 framepos_t start = min (pos, start_at_start);
5728 framepos_t end = max (pos, end_at_start);
5729 _editor->selection->set (start, end);
5732 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5733 if (_editor->clicked_selection) {
5734 _editor->selection->remove (_editor->clicked_selection);
5737 if (!_editor->clicked_selection) {
5738 _editor->selection->clear_time();
5743 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5744 _editor->selection->set (_editor->clicked_axisview);
5747 if (s && s->get_play_range () && s->transport_rolling()) {
5748 s->request_stop (false, false);
5753 _editor->stop_canvas_autoscroll ();
5754 _editor->clicked_selection = 0;
5755 _editor->commit_reversible_selection_op ();
5759 SelectionDrag::aborted (bool)
5764 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5765 : Drag (e, i, false),
5769 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5771 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5772 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5773 physical_screen_height (_editor->current_toplevel()->get_window())));
5774 _drag_rect->hide ();
5776 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5777 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5780 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5782 /* normal canvas items will be cleaned up when their parent group is deleted. But
5783 this item is created as the child of a long-lived parent group, and so we
5784 need to explicitly delete it.
5790 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5792 if (_editor->session() == 0) {
5796 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5798 if (!_editor->temp_location) {
5799 _editor->temp_location = new Location (*_editor->session());
5802 switch (_operation) {
5803 case CreateSkipMarker:
5804 case CreateRangeMarker:
5805 case CreateTransportMarker:
5806 case CreateCDMarker:
5808 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5813 cursor = _editor->cursors()->selector;
5817 Drag::start_grab (event, cursor);
5819 show_verbose_cursor_time (adjusted_current_frame (event));
5823 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5825 framepos_t start = 0;
5827 ArdourCanvas::Rectangle *crect;
5829 switch (_operation) {
5830 case CreateSkipMarker:
5831 crect = _editor->range_bar_drag_rect;
5833 case CreateRangeMarker:
5834 crect = _editor->range_bar_drag_rect;
5836 case CreateTransportMarker:
5837 crect = _editor->transport_bar_drag_rect;
5839 case CreateCDMarker:
5840 crect = _editor->cd_marker_bar_drag_rect;
5843 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5848 framepos_t const pf = adjusted_current_frame (event);
5850 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5851 MusicFrame grab (grab_frame (), 0);
5852 _editor->snap_to (grab);
5854 if (pf < grab_frame()) {
5862 /* first drag: Either add to the selection
5863 or create a new selection.
5868 _editor->temp_location->set (start, end);
5872 update_item (_editor->temp_location);
5874 //_drag_rect->raise_to_top();
5880 _editor->temp_location->set (start, end);
5882 double x1 = _editor->sample_to_pixel (start);
5883 double x2 = _editor->sample_to_pixel (end);
5887 update_item (_editor->temp_location);
5890 show_verbose_cursor_time (pf);
5895 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5897 Location * newloc = 0;
5901 if (movement_occurred) {
5902 motion (event, false);
5905 switch (_operation) {
5906 case CreateSkipMarker:
5907 case CreateRangeMarker:
5908 case CreateCDMarker:
5910 XMLNode &before = _editor->session()->locations()->get_state();
5911 if (_operation == CreateSkipMarker) {
5912 _editor->begin_reversible_command (_("new skip marker"));
5913 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5914 flags = Location::IsRangeMarker | Location::IsSkip;
5915 _editor->range_bar_drag_rect->hide();
5916 } else if (_operation == CreateCDMarker) {
5917 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5918 _editor->begin_reversible_command (_("new CD marker"));
5919 flags = Location::IsRangeMarker | Location::IsCDMarker;
5920 _editor->cd_marker_bar_drag_rect->hide();
5922 _editor->begin_reversible_command (_("new skip marker"));
5923 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5924 flags = Location::IsRangeMarker;
5925 _editor->range_bar_drag_rect->hide();
5927 newloc = new Location (
5928 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5929 , _editor->get_grid_music_divisions (event->button.state));
5931 _editor->session()->locations()->add (newloc, true);
5932 XMLNode &after = _editor->session()->locations()->get_state();
5933 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5934 _editor->commit_reversible_command ();
5938 case CreateTransportMarker:
5939 // popup menu to pick loop or punch
5940 _editor->new_transport_marker_context_menu (&event->button, _item);
5946 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5948 if (_operation == CreateTransportMarker) {
5950 /* didn't drag, so just locate */
5952 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5954 } else if (_operation == CreateCDMarker) {
5956 /* didn't drag, but mark is already created so do
5959 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5964 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5966 if (end == max_framepos) {
5967 end = _editor->session()->current_end_frame ();
5970 if (start == max_framepos) {
5971 start = _editor->session()->current_start_frame ();
5974 switch (_editor->mouse_mode) {
5976 /* find the two markers on either side and then make the selection from it */
5977 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5981 /* find the two markers on either side of the click and make the range out of it */
5982 _editor->selection->set (start, end);
5991 _editor->stop_canvas_autoscroll ();
5995 RangeMarkerBarDrag::aborted (bool movement_occurred)
5997 if (movement_occurred) {
5998 _drag_rect->hide ();
6003 RangeMarkerBarDrag::update_item (Location* location)
6005 double const x1 = _editor->sample_to_pixel (location->start());
6006 double const x2 = _editor->sample_to_pixel (location->end());
6008 _drag_rect->set_x0 (x1);
6009 _drag_rect->set_x1 (x2);
6012 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
6014 , _cumulative_dx (0)
6015 , _cumulative_dy (0)
6017 , _was_selected (false)
6020 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
6022 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
6024 _region = &_primary->region_view ();
6025 _note_height = _region->midi_stream_view()->note_height ();
6029 NoteDrag::setup_pointer_frame_offset ()
6031 _pointer_frame_offset = raw_grab_frame()
6032 - _editor->session()->tempo_map().frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
6036 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
6038 Drag::start_grab (event);
6040 if (ArdourKeyboard::indicates_copy (event->button.state)) {
6046 setup_snap_delta (MusicFrame (_region->source_beats_to_absolute_frames (_primary->note()->time ()), 0));
6048 if (!(_was_selected = _primary->selected())) {
6050 /* tertiary-click means extend selection - we'll do that on button release,
6051 so don't add it here, because otherwise we make it hard to figure
6052 out the "extend-to" range.
6055 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
6058 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
6061 _region->note_selected (_primary, true);
6063 _editor->get_selection().clear_points();
6064 _region->unique_select (_primary);
6070 /** @return Current total drag x change in quarter notes */
6072 NoteDrag::total_dx (GdkEvent * event) const
6074 if (_x_constrained) {
6078 TempoMap& map (_editor->session()->tempo_map());
6081 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
6083 /* primary note time */
6084 frameoffset_t const n = map.frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
6086 /* primary note time in quarter notes */
6087 double const n_qn = _region->session_relative_qn (_primary->note()->time().to_double());
6089 /* new time of the primary note in session frames */
6090 frameoffset_t st = n + dx + snap_delta (event->button.state);
6092 /* possibly snap and return corresponding delta in quarter notes */
6093 MusicFrame snap (st, 0);
6094 _editor->snap_to_with_modifier (snap, event);
6095 double ret = map.exact_qn_at_frame (snap.frame, snap.division) - n_qn - snap_delta_music (event->button.state);
6097 /* prevent the earliest note being dragged earlier than the region's start position */
6098 if (_earliest + ret < _region->midi_region()->start_beats()) {
6099 ret -= (_earliest + ret) - _region->midi_region()->start_beats();
6105 /** @return Current total drag y change in note number */
6107 NoteDrag::total_dy () const
6109 if (_y_constrained) {
6113 double const y = _region->midi_view()->y_position ();
6114 /* new current note */
6115 uint8_t n = _region->y_to_note (current_pointer_y () - y);
6117 MidiStreamView* msv = _region->midi_stream_view ();
6118 n = max (msv->lowest_note(), n);
6119 n = min (msv->highest_note(), n);
6120 /* and work out delta */
6121 return n - _region->y_to_note (grab_y() - y);
6125 NoteDrag::motion (GdkEvent * event, bool first_move)
6128 _earliest = _region->earliest_in_selection().to_double();
6130 /* make copies of all the selected notes */
6131 _primary = _region->copy_selection (_primary);
6135 /* Total change in x and y since the start of the drag */
6136 double const dx_qn = total_dx (event);
6137 int8_t const dy = total_dy ();
6139 /* Now work out what we have to do to the note canvas items to set this new drag delta */
6140 double const tdx = _x_constrained ? 0 : dx_qn - _cumulative_dx;
6141 double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
6144 _cumulative_dx = dx_qn;
6145 _cumulative_dy += tdy;
6147 int8_t note_delta = total_dy();
6151 _region->move_copies (dx_qn, tdy, note_delta);
6153 _region->move_selection (dx_qn, tdy, note_delta);
6156 /* the new note value may be the same as the old one, but we
6157 * don't know what that means because the selection may have
6158 * involved more than one note and we might be doing something
6159 * odd with them. so show the note value anyway, always.
6162 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
6164 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
6170 NoteDrag::finished (GdkEvent* ev, bool moved)
6173 /* no motion - select note */
6175 if (_editor->current_mouse_mode() == Editing::MouseContent ||
6176 _editor->current_mouse_mode() == Editing::MouseDraw) {
6178 bool changed = false;
6180 if (_was_selected) {
6181 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
6183 _region->note_deselected (_primary);
6186 _editor->get_selection().clear_points();
6187 _region->unique_select (_primary);
6191 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
6192 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
6194 if (!extend && !add && _region->selection_size() > 1) {
6195 _editor->get_selection().clear_points();
6196 _region->unique_select (_primary);
6198 } else if (extend) {
6199 _region->note_selected (_primary, true, true);
6202 /* it was added during button press */
6209 _editor->begin_reversible_selection_op(X_("Select Note Release"));
6210 _editor->commit_reversible_selection_op();
6214 _region->note_dropped (_primary, total_dx (ev), total_dy(), _copy);
6219 NoteDrag::aborted (bool)
6224 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
6225 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
6226 : Drag (editor, atv->base_item ())
6228 , _y_origin (atv->y_position())
6229 , _nothing_to_drag (false)
6231 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
6232 setup (atv->lines ());
6235 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
6236 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
6237 : Drag (editor, rv->get_canvas_group ())
6239 , _y_origin (rv->get_time_axis_view().y_position())
6240 , _nothing_to_drag (false)
6243 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
6245 list<boost::shared_ptr<AutomationLine> > lines;
6247 AudioRegionView* audio_view;
6248 AutomationRegionView* automation_view;
6249 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
6250 lines.push_back (audio_view->get_gain_line ());
6251 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
6252 lines.push_back (automation_view->line ());
6255 error << _("Automation range drag created for invalid region type") << endmsg;
6261 /** @param lines AutomationLines to drag.
6262 * @param offset Offset from the session start to the points in the AutomationLines.
6265 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
6267 /* find the lines that overlap the ranges being dragged */
6268 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
6269 while (i != lines.end ()) {
6270 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
6273 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
6275 /* check this range against all the AudioRanges that we are using */
6276 list<AudioRange>::const_iterator k = _ranges.begin ();
6277 while (k != _ranges.end()) {
6278 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
6284 /* add it to our list if it overlaps at all */
6285 if (k != _ranges.end()) {
6290 _lines.push_back (n);
6296 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
6300 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
6302 return 1.0 - ((global_y - _y_origin) / line->height());
6306 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
6308 const double v = list->eval(x);
6309 return _integral ? rint(v) : v;
6313 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6315 Drag::start_grab (event, cursor);
6317 /* Get line states before we start changing things */
6318 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6319 i->state = &i->line->get_state ();
6320 i->original_fraction = y_fraction (i->line, current_pointer_y());
6323 if (_ranges.empty()) {
6325 /* No selected time ranges: drag all points */
6326 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6327 uint32_t const N = i->line->npoints ();
6328 for (uint32_t j = 0; j < N; ++j) {
6329 i->points.push_back (i->line->nth (j));
6335 if (_nothing_to_drag) {
6341 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
6343 if (_nothing_to_drag && !first_move) {
6348 _editor->begin_reversible_command (_("automation range move"));
6350 if (!_ranges.empty()) {
6352 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
6354 framecnt_t const half = (i->start + i->end) / 2;
6356 /* find the line that this audio range starts in */
6357 list<Line>::iterator j = _lines.begin();
6358 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
6362 if (j != _lines.end()) {
6363 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6365 /* j is the line that this audio range starts in; fade into it;
6366 64 samples length plucked out of thin air.
6369 framepos_t a = i->start + 64;
6374 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
6375 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
6377 XMLNode &before = the_list->get_state();
6378 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6379 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6381 if (add_p || add_q) {
6382 _editor->session()->add_command (
6383 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6387 /* same thing for the end */
6390 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
6394 if (j != _lines.end()) {
6395 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6397 /* j is the line that this audio range starts in; fade out of it;
6398 64 samples length plucked out of thin air.
6401 framepos_t b = i->end - 64;
6406 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
6407 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
6409 XMLNode &before = the_list->get_state();
6410 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6411 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6413 if (add_p || add_q) {
6414 _editor->session()->add_command (
6415 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6420 _nothing_to_drag = true;
6422 /* Find all the points that should be dragged and put them in the relevant
6423 points lists in the Line structs.
6426 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6428 uint32_t const N = i->line->npoints ();
6429 for (uint32_t j = 0; j < N; ++j) {
6431 /* here's a control point on this line */
6432 ControlPoint* p = i->line->nth (j);
6433 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
6435 /* see if it's inside a range */
6436 list<AudioRange>::const_iterator k = _ranges.begin ();
6437 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
6441 if (k != _ranges.end()) {
6442 /* dragging this point */
6443 _nothing_to_drag = false;
6444 i->points.push_back (p);
6450 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6451 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
6455 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
6456 float const f = y_fraction (l->line, current_pointer_y());
6457 /* we are ignoring x position for this drag, so we can just pass in anything */
6458 pair<float, float> result;
6460 result = l->line->drag_motion (0, f, true, false, ignored);
6461 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (result.first, result.second));
6466 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
6468 if (_nothing_to_drag || !motion_occurred) {
6472 motion (event, false);
6473 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6474 i->line->end_drag (false, 0);
6477 _editor->commit_reversible_command ();
6481 AutomationRangeDrag::aborted (bool)
6483 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6488 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6490 , initial_time_axis_view (itav)
6492 /* note that time_axis_view may be null if the regionview was created
6493 * as part of a copy operation.
6495 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6496 layer = v->region()->layer ();
6497 initial_y = v->get_canvas_group()->position().y;
6498 initial_playlist = v->region()->playlist ();
6499 initial_position = v->region()->position ();
6500 initial_end = v->region()->position () + v->region()->length ();
6503 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6504 : Drag (e, i->canvas_item ())
6507 , _cumulative_dx (0)
6509 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6510 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6515 PatchChangeDrag::motion (GdkEvent* ev, bool)
6517 framepos_t f = adjusted_current_frame (ev);
6518 boost::shared_ptr<Region> r = _region_view->region ();
6519 f = max (f, r->position ());
6520 f = min (f, r->last_frame ());
6522 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6523 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6524 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6525 _cumulative_dx = dxu;
6529 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6531 if (!movement_occurred) {
6532 if (was_double_click()) {
6533 _region_view->edit_patch_change (_patch_change);
6538 boost::shared_ptr<Region> r (_region_view->region ());
6539 framepos_t f = adjusted_current_frame (ev);
6540 f = max (f, r->position ());
6541 f = min (f, r->last_frame ());
6543 _region_view->move_patch_change (
6545 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6550 PatchChangeDrag::aborted (bool)
6552 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6556 PatchChangeDrag::setup_pointer_frame_offset ()
6558 boost::shared_ptr<Region> region = _region_view->region ();
6559 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6562 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6563 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6570 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6572 _region_view->update_drag_selection (
6574 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6578 MidiRubberbandSelectDrag::deselect_things ()
6583 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6584 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6587 _vertical_only = true;
6591 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6593 double const y = _region_view->midi_view()->y_position ();
6595 y1 = max (0.0, y1 - y);
6596 y2 = max (0.0, y2 - y);
6598 _region_view->update_vertical_drag_selection (
6601 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6606 MidiVerticalSelectDrag::deselect_things ()
6611 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6612 : RubberbandSelectDrag (e, i)
6618 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6620 if (drag_in_progress) {
6621 /* We just want to select things at the end of the drag, not during it */
6625 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6627 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6629 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6631 _editor->commit_reversible_selection_op ();
6635 EditorRubberbandSelectDrag::deselect_things ()
6637 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6639 _editor->selection->clear_tracks();
6640 _editor->selection->clear_regions();
6641 _editor->selection->clear_points ();
6642 _editor->selection->clear_lines ();
6643 _editor->selection->clear_midi_notes ();
6645 _editor->commit_reversible_selection_op();
6648 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6653 _note[0] = _note[1] = 0;
6656 NoteCreateDrag::~NoteCreateDrag ()
6662 NoteCreateDrag::grid_frames (framepos_t t) const
6665 const Evoral::Beats grid_beats = _region_view->get_grid_beats (t);
6666 const Evoral::Beats t_beats = _region_view->region_frames_to_region_beats (t);
6668 return _region_view->region_beats_to_region_frames (t_beats + grid_beats)
6669 - _region_view->region_beats_to_region_frames (t_beats);
6673 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6675 Drag::start_grab (event, cursor);
6677 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6678 TempoMap& map (_editor->session()->tempo_map());
6680 const framepos_t pf = _drags->current_pointer_frame ();
6681 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6683 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6685 double eqaf = map.exact_qn_at_frame (pf, divisions);
6687 if (divisions != 0) {
6689 const double qaf = map.quarter_note_at_frame (pf);
6691 /* Hack so that we always snap to the note that we are over, instead of snapping
6692 to the next one if we're more than halfway through the one we're over.
6695 const double rem = eqaf - qaf;
6697 eqaf -= grid_beats.to_double();
6701 _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6702 /* minimum initial length is grid beats */
6703 _note[1] = map.frame_at_quarter_note (eqaf + grid_beats.to_double()) - _region_view->region()->position();
6705 double const x0 = _editor->sample_to_pixel (_note[0]);
6706 double const x1 = _editor->sample_to_pixel (_note[1]);
6707 double const y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6709 _drag_rect->set (ArdourCanvas::Rect (x0, y, x1, y + floor (_region_view->midi_stream_view()->note_height ())));
6710 _drag_rect->set_outline_all ();
6711 _drag_rect->set_outline_color (0xffffff99);
6712 _drag_rect->set_fill_color (0xffffff66);
6716 NoteCreateDrag::motion (GdkEvent* event, bool)
6718 TempoMap& map (_editor->session()->tempo_map());
6719 const framepos_t pf = _drags->current_pointer_frame ();
6720 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6721 double eqaf = map.exact_qn_at_frame (pf, divisions);
6723 if (divisions != 0) {
6725 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6727 const double qaf = map.quarter_note_at_frame (pf);
6728 /* Hack so that we always snap to the note that we are over, instead of snapping
6729 to the next one if we're more than halfway through the one we're over.
6732 const double rem = eqaf - qaf;
6734 eqaf -= grid_beats.to_double();
6737 eqaf += grid_beats.to_double();
6739 _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6741 double const x0 = _editor->sample_to_pixel (_note[0]);
6742 double const x1 = _editor->sample_to_pixel (_note[1]);
6743 _drag_rect->set_x0 (std::min(x0, x1));
6744 _drag_rect->set_x1 (std::max(x0, x1));
6748 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6750 /* we create a note even if there was no movement */
6751 framepos_t const start = min (_note[0], _note[1]);
6752 framepos_t const start_sess_rel = start + _region_view->region()->position();
6753 framecnt_t length = max (_editor->pixel_to_sample (1.0), (framecnt_t) fabs ((double)(_note[0] - _note[1])));
6754 framecnt_t const g = grid_frames (start_sess_rel);
6756 if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6760 TempoMap& map (_editor->session()->tempo_map());
6761 const double qn_length = map.quarter_notes_between_frames (start_sess_rel, start_sess_rel + length);
6762 Evoral::Beats qn_length_beats = max (Evoral::Beats::ticks(1), Evoral::Beats (qn_length));
6764 _editor->begin_reversible_command (_("Create Note"));
6765 _region_view->clear_editor_note_selection();
6766 _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6767 _editor->commit_reversible_command ();
6771 NoteCreateDrag::y_to_region (double y) const
6774 _region_view->get_canvas_group()->canvas_to_item (x, y);
6779 NoteCreateDrag::aborted (bool)
6784 HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6792 HitCreateDrag::~HitCreateDrag ()
6797 HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6799 Drag::start_grab (event, cursor);
6801 TempoMap& map (_editor->session()->tempo_map());
6803 _y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6805 const framepos_t pf = _drags->current_pointer_frame ();
6806 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6808 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6810 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6812 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6816 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6817 Evoral::Beats length = _region_view->get_grid_beats (pf);
6819 _editor->begin_reversible_command (_("Create Hit"));
6820 _region_view->clear_editor_note_selection();
6821 _region_view->create_note_at (start, _y, length, event->button.state, false);
6827 HitCreateDrag::motion (GdkEvent* event, bool)
6829 TempoMap& map (_editor->session()->tempo_map());
6831 const framepos_t pf = _drags->current_pointer_frame ();
6832 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6834 if (divisions == 0) {
6838 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6839 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position ();
6841 if (_last_pos == start) {
6845 Evoral::Beats length = _region_view->get_grid_beats (pf);
6847 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6849 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6853 _region_view->create_note_at (start, _y, length, event->button.state, false);
6859 HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */)
6861 _editor->commit_reversible_command ();
6866 HitCreateDrag::y_to_region (double y) const
6869 _region_view->get_canvas_group()->canvas_to_item (x, y);
6874 HitCreateDrag::aborted (bool)
6879 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6884 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6888 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6890 Drag::start_grab (event, cursor);
6894 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6900 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6903 distance = _drags->current_pointer_x() - grab_x();
6904 len = ar->fade_in()->back()->when;
6906 distance = grab_x() - _drags->current_pointer_x();
6907 len = ar->fade_out()->back()->when;
6910 /* how long should it be ? */
6912 new_length = len + _editor->pixel_to_sample (distance);
6914 /* now check with the region that this is legal */
6916 new_length = ar->verify_xfade_bounds (new_length, start);
6919 arv->reset_fade_in_shape_width (ar, new_length);
6921 arv->reset_fade_out_shape_width (ar, new_length);
6926 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6932 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6935 distance = _drags->current_pointer_x() - grab_x();
6936 len = ar->fade_in()->back()->when;
6938 distance = grab_x() - _drags->current_pointer_x();
6939 len = ar->fade_out()->back()->when;
6942 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6944 _editor->begin_reversible_command ("xfade trim");
6945 ar->playlist()->clear_owned_changes ();
6948 ar->set_fade_in_length (new_length);
6950 ar->set_fade_out_length (new_length);
6953 /* Adjusting the xfade may affect other regions in the playlist, so we need
6954 to get undo Commands from the whole playlist rather than just the
6958 vector<Command*> cmds;
6959 ar->playlist()->rdiff (cmds);
6960 _editor->session()->add_commands (cmds);
6961 _editor->commit_reversible_command ();
6966 CrossfadeEdgeDrag::aborted (bool)
6969 // arv->redraw_start_xfade ();
6971 // arv->redraw_end_xfade ();
6975 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6976 : Drag (e, item, true)
6977 , line (new EditorCursor (*e))
6979 line->set_position (pos);
6981 line->track_canvas_item().reparent (_editor->_drag_motion_group);
6984 RegionCutDrag::~RegionCutDrag ()
6990 RegionCutDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6992 Drag::start_grab (event, c);
6993 motion (event, false);
6997 RegionCutDrag::motion (GdkEvent* event, bool)
6999 MusicFrame pos (_drags->current_pointer_frame(), 0);
7000 _editor->snap_to_with_modifier (pos, event);
7002 line->set_position (pos.frame);
7006 RegionCutDrag::finished (GdkEvent* event, bool)
7008 _editor->get_track_canvas()->canvas()->re_enter();
7011 MusicFrame pos (_drags->current_pointer_frame(), 0);
7012 _editor->snap_to_with_modifier (pos, event);
7015 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos.frame);
7021 _editor->split_regions_at (pos, rs, false);
7025 RegionCutDrag::aborted (bool)